roojs-bootstrap.js
[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     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <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>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         
2523         var dom = move_card.el.dom;
2524         dom.parentNode.removeChild(dom);
2525         dom.style.width = ''; // clear with - which is set by drag.
2526         
2527         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2528             var cardel = next_to_card.el.dom;
2529             
2530             if (position == 'above' ) {
2531                 cardel.parentNode.insertBefore(dom, cardel);
2532             } else if (cardel.nextSibling) {
2533                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2534             } else {
2535                 cardel.parentNode.append(dom);
2536             }
2537         } else {
2538             // card container???
2539             this.containerEl.dom.append(dom);
2540         }
2541         
2542         //FIXME HANDLE card = true 
2543         
2544         // add this to the correct place in items.
2545         
2546         
2547         
2548         // remove Card from items.
2549         
2550         var old_parent = move_card.parent();
2551         
2552         old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2553         
2554         if (this.items.length) {
2555             var nitems = [];
2556             //Roo.log([info.items_n, info.position, this.items.length]);
2557             for (var i =0; i < this.items.length; i++) {
2558                 if (i == to_items_n && position == 'above') {
2559                     nitems.push(move_card);
2560                 }
2561                 nitems.push(this.items[i]);
2562                 if (i == to_items_n && position == 'below') {
2563                     nitems.push(move_card);
2564                 }
2565             }
2566             this.items = nitems;
2567             Roo.log(this.items);
2568         } else {
2569             this.items.push(move_card);
2570         }
2571         
2572         move_card.parentId = this.id;
2573         
2574         return true;
2575         
2576         
2577     },
2578     
2579     
2580     /**    Decide whether to drop above or below a View node. */
2581     getDropPoint : function(e, n, dd)
2582     {
2583         if (dd) {
2584              return false;
2585         }
2586         if (n == this.containerEl.dom) {
2587             return "above";
2588         }
2589         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2590         var c = t + (b - t) / 2;
2591         var y = Roo.lib.Event.getPageY(e);
2592         if(y <= c) {
2593             return "above";
2594         }else{
2595             return "below";
2596         }
2597     },
2598     onToggleCollapse : function(e)
2599         {
2600         if (this.collapsed) {
2601             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2602             this.collapsableEl.addClass('show');
2603             this.collapsed = false;
2604             return;
2605         }
2606         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2607         this.collapsableEl.removeClass('show');
2608         this.collapsed = true;
2609         
2610     
2611     },
2612     
2613     onToggleRotate : function(e)
2614     {
2615         this.collapsableEl.removeClass('show');
2616         this.footerEl.removeClass('d-none');
2617         this.el.removeClass('roo-card-rotated');
2618         this.el.removeClass('d-none');
2619         if (this.rotated) {
2620             
2621             this.collapsableEl.addClass('show');
2622             this.rotated = false;
2623             this.fireEvent('rotate', this, this.rotated);
2624             return;
2625         }
2626         this.el.addClass('roo-card-rotated');
2627         this.footerEl.addClass('d-none');
2628         this.el.select('.roo-collapsable').removeClass('show');
2629         
2630         this.rotated = true;
2631         this.fireEvent('rotate', this, this.rotated);
2632     
2633     },
2634     
2635     dropPlaceHolder: function (action, info, data)
2636     {
2637         if (this.dropEl === false) {
2638             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2639             cls : 'd-none'
2640             },true);
2641         }
2642         this.dropEl.removeClass(['d-none', 'd-block']);        
2643         if (action == 'hide') {
2644             
2645             this.dropEl.addClass('d-none');
2646             return;
2647         }
2648         // FIXME - info.card == true!!!
2649         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2650         
2651         if (info.card !== true) {
2652             var cardel = info.card.el.dom;
2653             
2654             if (info.position == 'above') {
2655                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2656             } else if (cardel.nextSibling) {
2657                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2658             } else {
2659                 cardel.parentNode.append(this.dropEl.dom);
2660             }
2661         } else {
2662             // card container???
2663             this.containerEl.dom.append(this.dropEl.dom);
2664         }
2665         
2666         this.dropEl.addClass('d-block roo-card-dropzone');
2667         
2668         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2669         
2670         
2671     
2672     
2673     
2674     },
2675     setHeaderText: function(html)
2676     {
2677         this.headerContainerEl.dom.innerHTML = html;
2678     }
2679
2680     
2681 });
2682
2683 /*
2684  * - LGPL
2685  *
2686  * Card header - holder for the card header elements.
2687  * 
2688  */
2689
2690 /**
2691  * @class Roo.bootstrap.CardHeader
2692  * @extends Roo.bootstrap.Element
2693  * Bootstrap CardHeader class
2694  * @constructor
2695  * Create a new Card Header - that you can embed children into
2696  * @param {Object} config The config object
2697  */
2698
2699 Roo.bootstrap.CardHeader = function(config){
2700     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2701 };
2702
2703 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2704     
2705     
2706     container_method : 'getCardHeader' 
2707     
2708      
2709     
2710     
2711    
2712 });
2713
2714  
2715
2716  /*
2717  * - LGPL
2718  *
2719  * Card footer - holder for the card footer elements.
2720  * 
2721  */
2722
2723 /**
2724  * @class Roo.bootstrap.CardFooter
2725  * @extends Roo.bootstrap.Element
2726  * Bootstrap CardFooter class
2727  * @constructor
2728  * Create a new Card Footer - that you can embed children into
2729  * @param {Object} config The config object
2730  */
2731
2732 Roo.bootstrap.CardFooter = function(config){
2733     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2734 };
2735
2736 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2737     
2738     
2739     container_method : 'getCardFooter' 
2740     
2741      
2742     
2743     
2744    
2745 });
2746
2747  
2748
2749  /*
2750  * - LGPL
2751  *
2752  * Card header - holder for the card header elements.
2753  * 
2754  */
2755
2756 /**
2757  * @class Roo.bootstrap.CardImageTop
2758  * @extends Roo.bootstrap.Element
2759  * Bootstrap CardImageTop class
2760  * @constructor
2761  * Create a new Card Image Top container
2762  * @param {Object} config The config object
2763  */
2764
2765 Roo.bootstrap.CardImageTop = function(config){
2766     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2767 };
2768
2769 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2770     
2771    
2772     container_method : 'getCardImageTop' 
2773     
2774      
2775     
2776    
2777 });
2778
2779  
2780
2781  /*
2782  * - LGPL
2783  *
2784  * image
2785  * 
2786  */
2787
2788
2789 /**
2790  * @class Roo.bootstrap.Img
2791  * @extends Roo.bootstrap.Component
2792  * Bootstrap Img class
2793  * @cfg {Boolean} imgResponsive false | true
2794  * @cfg {String} border rounded | circle | thumbnail
2795  * @cfg {String} src image source
2796  * @cfg {String} alt image alternative text
2797  * @cfg {String} href a tag href
2798  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2799  * @cfg {String} xsUrl xs image source
2800  * @cfg {String} smUrl sm image source
2801  * @cfg {String} mdUrl md image source
2802  * @cfg {String} lgUrl lg image source
2803  * 
2804  * @constructor
2805  * Create a new Input
2806  * @param {Object} config The config object
2807  */
2808
2809 Roo.bootstrap.Img = function(config){
2810     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2811     
2812     this.addEvents({
2813         // img events
2814         /**
2815          * @event click
2816          * The img click event for the img.
2817          * @param {Roo.EventObject} e
2818          */
2819         "click" : true
2820     });
2821 };
2822
2823 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2824     
2825     imgResponsive: true,
2826     border: '',
2827     src: 'about:blank',
2828     href: false,
2829     target: false,
2830     xsUrl: '',
2831     smUrl: '',
2832     mdUrl: '',
2833     lgUrl: '',
2834
2835     getAutoCreate : function()
2836     {   
2837         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2838             return this.createSingleImg();
2839         }
2840         
2841         var cfg = {
2842             tag: 'div',
2843             cls: 'roo-image-responsive-group',
2844             cn: []
2845         };
2846         var _this = this;
2847         
2848         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2849             
2850             if(!_this[size + 'Url']){
2851                 return;
2852             }
2853             
2854             var img = {
2855                 tag: 'img',
2856                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2857                 html: _this.html || cfg.html,
2858                 src: _this[size + 'Url']
2859             };
2860             
2861             img.cls += ' roo-image-responsive-' + size;
2862             
2863             var s = ['xs', 'sm', 'md', 'lg'];
2864             
2865             s.splice(s.indexOf(size), 1);
2866             
2867             Roo.each(s, function(ss){
2868                 img.cls += ' hidden-' + ss;
2869             });
2870             
2871             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2872                 cfg.cls += ' img-' + _this.border;
2873             }
2874             
2875             if(_this.alt){
2876                 cfg.alt = _this.alt;
2877             }
2878             
2879             if(_this.href){
2880                 var a = {
2881                     tag: 'a',
2882                     href: _this.href,
2883                     cn: [
2884                         img
2885                     ]
2886                 };
2887
2888                 if(this.target){
2889                     a.target = _this.target;
2890                 }
2891             }
2892             
2893             cfg.cn.push((_this.href) ? a : img);
2894             
2895         });
2896         
2897         return cfg;
2898     },
2899     
2900     createSingleImg : function()
2901     {
2902         var cfg = {
2903             tag: 'img',
2904             cls: (this.imgResponsive) ? 'img-responsive' : '',
2905             html : null,
2906             src : 'about:blank'  // just incase src get's set to undefined?!?
2907         };
2908         
2909         cfg.html = this.html || cfg.html;
2910         
2911         cfg.src = this.src || cfg.src;
2912         
2913         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2914             cfg.cls += ' img-' + this.border;
2915         }
2916         
2917         if(this.alt){
2918             cfg.alt = this.alt;
2919         }
2920         
2921         if(this.href){
2922             var a = {
2923                 tag: 'a',
2924                 href: this.href,
2925                 cn: [
2926                     cfg
2927                 ]
2928             };
2929             
2930             if(this.target){
2931                 a.target = this.target;
2932             }
2933             
2934         }
2935         
2936         return (this.href) ? a : cfg;
2937     },
2938     
2939     initEvents: function() 
2940     {
2941         if(!this.href){
2942             this.el.on('click', this.onClick, this);
2943         }
2944         
2945     },
2946     
2947     onClick : function(e)
2948     {
2949         Roo.log('img onclick');
2950         this.fireEvent('click', this, e);
2951     },
2952     /**
2953      * Sets the url of the image - used to update it
2954      * @param {String} url the url of the image
2955      */
2956     
2957     setSrc : function(url)
2958     {
2959         this.src =  url;
2960         
2961         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2962             this.el.dom.src =  url;
2963             return;
2964         }
2965         
2966         this.el.select('img', true).first().dom.src =  url;
2967     }
2968     
2969     
2970    
2971 });
2972
2973  /*
2974  * - LGPL
2975  *
2976  * image
2977  * 
2978  */
2979
2980
2981 /**
2982  * @class Roo.bootstrap.Link
2983  * @extends Roo.bootstrap.Component
2984  * Bootstrap Link Class
2985  * @cfg {String} alt image alternative text
2986  * @cfg {String} href a tag href
2987  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2988  * @cfg {String} html the content of the link.
2989  * @cfg {String} anchor name for the anchor link
2990  * @cfg {String} fa - favicon
2991
2992  * @cfg {Boolean} preventDefault (true | false) default false
2993
2994  * 
2995  * @constructor
2996  * Create a new Input
2997  * @param {Object} config The config object
2998  */
2999
3000 Roo.bootstrap.Link = function(config){
3001     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3002     
3003     this.addEvents({
3004         // img events
3005         /**
3006          * @event click
3007          * The img click event for the img.
3008          * @param {Roo.EventObject} e
3009          */
3010         "click" : true
3011     });
3012 };
3013
3014 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3015     
3016     href: false,
3017     target: false,
3018     preventDefault: false,
3019     anchor : false,
3020     alt : false,
3021     fa: false,
3022
3023
3024     getAutoCreate : function()
3025     {
3026         var html = this.html || '';
3027         
3028         if (this.fa !== false) {
3029             html = '<i class="fa fa-' + this.fa + '"></i>';
3030         }
3031         var cfg = {
3032             tag: 'a'
3033         };
3034         // anchor's do not require html/href...
3035         if (this.anchor === false) {
3036             cfg.html = html;
3037             cfg.href = this.href || '#';
3038         } else {
3039             cfg.name = this.anchor;
3040             if (this.html !== false || this.fa !== false) {
3041                 cfg.html = html;
3042             }
3043             if (this.href !== false) {
3044                 cfg.href = this.href;
3045             }
3046         }
3047         
3048         if(this.alt !== false){
3049             cfg.alt = this.alt;
3050         }
3051         
3052         
3053         if(this.target !== false) {
3054             cfg.target = this.target;
3055         }
3056         
3057         return cfg;
3058     },
3059     
3060     initEvents: function() {
3061         
3062         if(!this.href || this.preventDefault){
3063             this.el.on('click', this.onClick, this);
3064         }
3065     },
3066     
3067     onClick : function(e)
3068     {
3069         if(this.preventDefault){
3070             e.preventDefault();
3071         }
3072         //Roo.log('img onclick');
3073         this.fireEvent('click', this, e);
3074     }
3075    
3076 });
3077
3078  /*
3079  * - LGPL
3080  *
3081  * header
3082  * 
3083  */
3084
3085 /**
3086  * @class Roo.bootstrap.Header
3087  * @extends Roo.bootstrap.Component
3088  * Bootstrap Header class
3089  * @cfg {String} html content of header
3090  * @cfg {Number} level (1|2|3|4|5|6) default 1
3091  * 
3092  * @constructor
3093  * Create a new Header
3094  * @param {Object} config The config object
3095  */
3096
3097
3098 Roo.bootstrap.Header  = function(config){
3099     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3100 };
3101
3102 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3103     
3104     //href : false,
3105     html : false,
3106     level : 1,
3107     
3108     
3109     
3110     getAutoCreate : function(){
3111         
3112         
3113         
3114         var cfg = {
3115             tag: 'h' + (1 *this.level),
3116             html: this.html || ''
3117         } ;
3118         
3119         return cfg;
3120     }
3121    
3122 });
3123
3124  
3125
3126  /*
3127  * Based on:
3128  * Ext JS Library 1.1.1
3129  * Copyright(c) 2006-2007, Ext JS, LLC.
3130  *
3131  * Originally Released Under LGPL - original licence link has changed is not relivant.
3132  *
3133  * Fork - LGPL
3134  * <script type="text/javascript">
3135  */
3136  
3137 /**
3138  * @class Roo.bootstrap.MenuMgr
3139  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3140  * @singleton
3141  */
3142 Roo.bootstrap.MenuMgr = function(){
3143    var menus, active, groups = {}, attached = false, lastShow = new Date();
3144
3145    // private - called when first menu is created
3146    function init(){
3147        menus = {};
3148        active = new Roo.util.MixedCollection();
3149        Roo.get(document).addKeyListener(27, function(){
3150            if(active.length > 0){
3151                hideAll();
3152            }
3153        });
3154    }
3155
3156    // private
3157    function hideAll(){
3158        if(active && active.length > 0){
3159            var c = active.clone();
3160            c.each(function(m){
3161                m.hide();
3162            });
3163        }
3164    }
3165
3166    // private
3167    function onHide(m){
3168        active.remove(m);
3169        if(active.length < 1){
3170            Roo.get(document).un("mouseup", onMouseDown);
3171             
3172            attached = false;
3173        }
3174    }
3175
3176    // private
3177    function onShow(m){
3178        var last = active.last();
3179        lastShow = new Date();
3180        active.add(m);
3181        if(!attached){
3182           Roo.get(document).on("mouseup", onMouseDown);
3183            
3184            attached = true;
3185        }
3186        if(m.parentMenu){
3187           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3188           m.parentMenu.activeChild = m;
3189        }else if(last && last.isVisible()){
3190           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3191        }
3192    }
3193
3194    // private
3195    function onBeforeHide(m){
3196        if(m.activeChild){
3197            m.activeChild.hide();
3198        }
3199        if(m.autoHideTimer){
3200            clearTimeout(m.autoHideTimer);
3201            delete m.autoHideTimer;
3202        }
3203    }
3204
3205    // private
3206    function onBeforeShow(m){
3207        var pm = m.parentMenu;
3208        if(!pm && !m.allowOtherMenus){
3209            hideAll();
3210        }else if(pm && pm.activeChild && active != m){
3211            pm.activeChild.hide();
3212        }
3213    }
3214
3215    // private this should really trigger on mouseup..
3216    function onMouseDown(e){
3217         Roo.log("on Mouse Up");
3218         
3219         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3220             Roo.log("MenuManager hideAll");
3221             hideAll();
3222             e.stopEvent();
3223         }
3224         
3225         
3226    }
3227
3228    // private
3229    function onBeforeCheck(mi, state){
3230        if(state){
3231            var g = groups[mi.group];
3232            for(var i = 0, l = g.length; i < l; i++){
3233                if(g[i] != mi){
3234                    g[i].setChecked(false);
3235                }
3236            }
3237        }
3238    }
3239
3240    return {
3241
3242        /**
3243         * Hides all menus that are currently visible
3244         */
3245        hideAll : function(){
3246             hideAll();  
3247        },
3248
3249        // private
3250        register : function(menu){
3251            if(!menus){
3252                init();
3253            }
3254            menus[menu.id] = menu;
3255            menu.on("beforehide", onBeforeHide);
3256            menu.on("hide", onHide);
3257            menu.on("beforeshow", onBeforeShow);
3258            menu.on("show", onShow);
3259            var g = menu.group;
3260            if(g && menu.events["checkchange"]){
3261                if(!groups[g]){
3262                    groups[g] = [];
3263                }
3264                groups[g].push(menu);
3265                menu.on("checkchange", onCheck);
3266            }
3267        },
3268
3269         /**
3270          * Returns a {@link Roo.menu.Menu} object
3271          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3272          * be used to generate and return a new Menu instance.
3273          */
3274        get : function(menu){
3275            if(typeof menu == "string"){ // menu id
3276                return menus[menu];
3277            }else if(menu.events){  // menu instance
3278                return menu;
3279            }
3280            /*else if(typeof menu.length == 'number'){ // array of menu items?
3281                return new Roo.bootstrap.Menu({items:menu});
3282            }else{ // otherwise, must be a config
3283                return new Roo.bootstrap.Menu(menu);
3284            }
3285            */
3286            return false;
3287        },
3288
3289        // private
3290        unregister : function(menu){
3291            delete menus[menu.id];
3292            menu.un("beforehide", onBeforeHide);
3293            menu.un("hide", onHide);
3294            menu.un("beforeshow", onBeforeShow);
3295            menu.un("show", onShow);
3296            var g = menu.group;
3297            if(g && menu.events["checkchange"]){
3298                groups[g].remove(menu);
3299                menu.un("checkchange", onCheck);
3300            }
3301        },
3302
3303        // private
3304        registerCheckable : function(menuItem){
3305            var g = menuItem.group;
3306            if(g){
3307                if(!groups[g]){
3308                    groups[g] = [];
3309                }
3310                groups[g].push(menuItem);
3311                menuItem.on("beforecheckchange", onBeforeCheck);
3312            }
3313        },
3314
3315        // private
3316        unregisterCheckable : function(menuItem){
3317            var g = menuItem.group;
3318            if(g){
3319                groups[g].remove(menuItem);
3320                menuItem.un("beforecheckchange", onBeforeCheck);
3321            }
3322        }
3323    };
3324 }();/*
3325  * - LGPL
3326  *
3327  * menu
3328  * 
3329  */
3330
3331 /**
3332  * @class Roo.bootstrap.Menu
3333  * @extends Roo.bootstrap.Component
3334  * Bootstrap Menu class - container for MenuItems
3335  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3336  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3337  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3338  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3339  * 
3340  * @constructor
3341  * Create a new Menu
3342  * @param {Object} config The config object
3343  */
3344
3345
3346 Roo.bootstrap.Menu = function(config){
3347     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3348     if (this.registerMenu && this.type != 'treeview')  {
3349         Roo.bootstrap.MenuMgr.register(this);
3350     }
3351     
3352     
3353     this.addEvents({
3354         /**
3355          * @event beforeshow
3356          * Fires before this menu is displayed (return false to block)
3357          * @param {Roo.menu.Menu} this
3358          */
3359         beforeshow : true,
3360         /**
3361          * @event beforehide
3362          * Fires before this menu is hidden (return false to block)
3363          * @param {Roo.menu.Menu} this
3364          */
3365         beforehide : true,
3366         /**
3367          * @event show
3368          * Fires after this menu is displayed
3369          * @param {Roo.menu.Menu} this
3370          */
3371         show : true,
3372         /**
3373          * @event hide
3374          * Fires after this menu is hidden
3375          * @param {Roo.menu.Menu} this
3376          */
3377         hide : true,
3378         /**
3379          * @event click
3380          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3381          * @param {Roo.menu.Menu} this
3382          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3383          * @param {Roo.EventObject} e
3384          */
3385         click : true,
3386         /**
3387          * @event mouseover
3388          * Fires when the mouse is hovering over this menu
3389          * @param {Roo.menu.Menu} this
3390          * @param {Roo.EventObject} e
3391          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3392          */
3393         mouseover : true,
3394         /**
3395          * @event mouseout
3396          * Fires when the mouse exits this menu
3397          * @param {Roo.menu.Menu} this
3398          * @param {Roo.EventObject} e
3399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3400          */
3401         mouseout : true,
3402         /**
3403          * @event itemclick
3404          * Fires when a menu item contained in this menu is clicked
3405          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3406          * @param {Roo.EventObject} e
3407          */
3408         itemclick: true
3409     });
3410     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3411 };
3412
3413 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3414     
3415    /// html : false,
3416     //align : '',
3417     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3418     type: false,
3419     /**
3420      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3421      */
3422     registerMenu : true,
3423     
3424     menuItems :false, // stores the menu items..
3425     
3426     hidden:true,
3427         
3428     parentMenu : false,
3429     
3430     stopEvent : true,
3431     
3432     isLink : false,
3433     
3434     getChildContainer : function() {
3435         return this.el;  
3436     },
3437     
3438     getAutoCreate : function(){
3439          
3440         //if (['right'].indexOf(this.align)!==-1) {
3441         //    cfg.cn[1].cls += ' pull-right'
3442         //}
3443         
3444         
3445         var cfg = {
3446             tag : 'ul',
3447             cls : 'dropdown-menu' ,
3448             style : 'z-index:1000'
3449             
3450         };
3451         
3452         if (this.type === 'submenu') {
3453             cfg.cls = 'submenu active';
3454         }
3455         if (this.type === 'treeview') {
3456             cfg.cls = 'treeview-menu';
3457         }
3458         
3459         return cfg;
3460     },
3461     initEvents : function() {
3462         
3463        // Roo.log("ADD event");
3464        // Roo.log(this.triggerEl.dom);
3465         
3466         this.triggerEl.on('click', this.onTriggerClick, this);
3467         
3468         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3469         
3470         
3471         if (this.triggerEl.hasClass('nav-item')) {
3472             // dropdown toggle on the 'a' in BS4?
3473             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3474         } else {
3475             this.triggerEl.addClass('dropdown-toggle');
3476         }
3477         if (Roo.isTouch) {
3478             this.el.on('touchstart'  , this.onTouch, this);
3479         }
3480         this.el.on('click' , this.onClick, this);
3481
3482         this.el.on("mouseover", this.onMouseOver, this);
3483         this.el.on("mouseout", this.onMouseOut, this);
3484         
3485     },
3486     
3487     findTargetItem : function(e)
3488     {
3489         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3490         if(!t){
3491             return false;
3492         }
3493         //Roo.log(t);         Roo.log(t.id);
3494         if(t && t.id){
3495             //Roo.log(this.menuitems);
3496             return this.menuitems.get(t.id);
3497             
3498             //return this.items.get(t.menuItemId);
3499         }
3500         
3501         return false;
3502     },
3503     
3504     onTouch : function(e) 
3505     {
3506         Roo.log("menu.onTouch");
3507         //e.stopEvent(); this make the user popdown broken
3508         this.onClick(e);
3509     },
3510     
3511     onClick : function(e)
3512     {
3513         Roo.log("menu.onClick");
3514         
3515         var t = this.findTargetItem(e);
3516         if(!t || t.isContainer){
3517             return;
3518         }
3519         Roo.log(e);
3520         /*
3521         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3522             if(t == this.activeItem && t.shouldDeactivate(e)){
3523                 this.activeItem.deactivate();
3524                 delete this.activeItem;
3525                 return;
3526             }
3527             if(t.canActivate){
3528                 this.setActiveItem(t, true);
3529             }
3530             return;
3531             
3532             
3533         }
3534         */
3535        
3536         Roo.log('pass click event');
3537         
3538         t.onClick(e);
3539         
3540         this.fireEvent("click", this, t, e);
3541         
3542         var _this = this;
3543         
3544         if(!t.href.length || t.href == '#'){
3545             (function() { _this.hide(); }).defer(100);
3546         }
3547         
3548     },
3549     
3550     onMouseOver : function(e){
3551         var t  = this.findTargetItem(e);
3552         //Roo.log(t);
3553         //if(t){
3554         //    if(t.canActivate && !t.disabled){
3555         //        this.setActiveItem(t, true);
3556         //    }
3557         //}
3558         
3559         this.fireEvent("mouseover", this, e, t);
3560     },
3561     isVisible : function(){
3562         return !this.hidden;
3563     },
3564     onMouseOut : function(e){
3565         var t  = this.findTargetItem(e);
3566         
3567         //if(t ){
3568         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3569         //        this.activeItem.deactivate();
3570         //        delete this.activeItem;
3571         //    }
3572         //}
3573         this.fireEvent("mouseout", this, e, t);
3574     },
3575     
3576     
3577     /**
3578      * Displays this menu relative to another element
3579      * @param {String/HTMLElement/Roo.Element} element The element to align to
3580      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3581      * the element (defaults to this.defaultAlign)
3582      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3583      */
3584     show : function(el, pos, parentMenu)
3585     {
3586         if (false === this.fireEvent("beforeshow", this)) {
3587             Roo.log("show canceled");
3588             return;
3589         }
3590         this.parentMenu = parentMenu;
3591         if(!this.el){
3592             this.render();
3593         }
3594         
3595         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3596     },
3597      /**
3598      * Displays this menu at a specific xy position
3599      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3600      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3601      */
3602     showAt : function(xy, parentMenu, /* private: */_e){
3603         this.parentMenu = parentMenu;
3604         if(!this.el){
3605             this.render();
3606         }
3607         if(_e !== false){
3608             this.fireEvent("beforeshow", this);
3609             //xy = this.el.adjustForConstraints(xy);
3610         }
3611         
3612         //this.el.show();
3613         this.hideMenuItems();
3614         this.hidden = false;
3615         this.triggerEl.addClass('open');
3616         this.el.addClass('show');
3617         
3618         // reassign x when hitting right
3619         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3620             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3621         }
3622         
3623         // reassign y when hitting bottom
3624         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3625             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3626         }
3627         
3628         // but the list may align on trigger left or trigger top... should it be a properity?
3629         
3630         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3631             this.el.setXY(xy);
3632         }
3633         
3634         this.focus();
3635         this.fireEvent("show", this);
3636     },
3637     
3638     focus : function(){
3639         return;
3640         if(!this.hidden){
3641             this.doFocus.defer(50, this);
3642         }
3643     },
3644
3645     doFocus : function(){
3646         if(!this.hidden){
3647             this.focusEl.focus();
3648         }
3649     },
3650
3651     /**
3652      * Hides this menu and optionally all parent menus
3653      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3654      */
3655     hide : function(deep)
3656     {
3657         if (false === this.fireEvent("beforehide", this)) {
3658             Roo.log("hide canceled");
3659             return;
3660         }
3661         this.hideMenuItems();
3662         if(this.el && this.isVisible()){
3663            
3664             if(this.activeItem){
3665                 this.activeItem.deactivate();
3666                 this.activeItem = null;
3667             }
3668             this.triggerEl.removeClass('open');;
3669             this.el.removeClass('show');
3670             this.hidden = true;
3671             this.fireEvent("hide", this);
3672         }
3673         if(deep === true && this.parentMenu){
3674             this.parentMenu.hide(true);
3675         }
3676     },
3677     
3678     onTriggerClick : function(e)
3679     {
3680         Roo.log('trigger click');
3681         
3682         var target = e.getTarget();
3683         
3684         Roo.log(target.nodeName.toLowerCase());
3685         
3686         if(target.nodeName.toLowerCase() === 'i'){
3687             e.preventDefault();
3688         }
3689         
3690     },
3691     
3692     onTriggerPress  : function(e)
3693     {
3694         Roo.log('trigger press');
3695         //Roo.log(e.getTarget());
3696        // Roo.log(this.triggerEl.dom);
3697        
3698         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3699         var pel = Roo.get(e.getTarget());
3700         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3701             Roo.log('is treeview or dropdown?');
3702             return;
3703         }
3704         
3705         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3706             return;
3707         }
3708         
3709         if (this.isVisible()) {
3710             Roo.log('hide');
3711             this.hide();
3712         } else {
3713             Roo.log('show');
3714             this.show(this.triggerEl, '?', false);
3715         }
3716         
3717         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3718             e.stopEvent();
3719         }
3720         
3721     },
3722        
3723     
3724     hideMenuItems : function()
3725     {
3726         Roo.log("hide Menu Items");
3727         if (!this.el) { 
3728             return;
3729         }
3730         
3731         this.el.select('.open',true).each(function(aa) {
3732             
3733             aa.removeClass('open');
3734          
3735         });
3736     },
3737     addxtypeChild : function (tree, cntr) {
3738         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3739           
3740         this.menuitems.add(comp);
3741         return comp;
3742
3743     },
3744     getEl : function()
3745     {
3746         Roo.log(this.el);
3747         return this.el;
3748     },
3749     
3750     clear : function()
3751     {
3752         this.getEl().dom.innerHTML = '';
3753         this.menuitems.clear();
3754     }
3755 });
3756
3757  
3758  /*
3759  * - LGPL
3760  *
3761  * menu item
3762  * 
3763  */
3764
3765
3766 /**
3767  * @class Roo.bootstrap.MenuItem
3768  * @extends Roo.bootstrap.Component
3769  * Bootstrap MenuItem class
3770  * @cfg {String} html the menu label
3771  * @cfg {String} href the link
3772  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3773  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3774  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3775  * @cfg {String} fa favicon to show on left of menu item.
3776  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3777  * 
3778  * 
3779  * @constructor
3780  * Create a new MenuItem
3781  * @param {Object} config The config object
3782  */
3783
3784
3785 Roo.bootstrap.MenuItem = function(config){
3786     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3787     this.addEvents({
3788         // raw events
3789         /**
3790          * @event click
3791          * The raw click event for the entire grid.
3792          * @param {Roo.bootstrap.MenuItem} this
3793          * @param {Roo.EventObject} e
3794          */
3795         "click" : true
3796     });
3797 };
3798
3799 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3800     
3801     href : false,
3802     html : false,
3803     preventDefault: false,
3804     isContainer : false,
3805     active : false,
3806     fa: false,
3807     
3808     getAutoCreate : function(){
3809         
3810         if(this.isContainer){
3811             return {
3812                 tag: 'li',
3813                 cls: 'dropdown-menu-item '
3814             };
3815         }
3816         var ctag = {
3817             tag: 'span',
3818             html: 'Link'
3819         };
3820         
3821         var anc = {
3822             tag : 'a',
3823             cls : 'dropdown-item',
3824             href : '#',
3825             cn : [  ]
3826         };
3827         
3828         if (this.fa !== false) {
3829             anc.cn.push({
3830                 tag : 'i',
3831                 cls : 'fa fa-' + this.fa
3832             });
3833         }
3834         
3835         anc.cn.push(ctag);
3836         
3837         
3838         var cfg= {
3839             tag: 'li',
3840             cls: 'dropdown-menu-item',
3841             cn: [ anc ]
3842         };
3843         if (this.parent().type == 'treeview') {
3844             cfg.cls = 'treeview-menu';
3845         }
3846         if (this.active) {
3847             cfg.cls += ' active';
3848         }
3849         
3850         
3851         
3852         anc.href = this.href || cfg.cn[0].href ;
3853         ctag.html = this.html || cfg.cn[0].html ;
3854         return cfg;
3855     },
3856     
3857     initEvents: function()
3858     {
3859         if (this.parent().type == 'treeview') {
3860             this.el.select('a').on('click', this.onClick, this);
3861         }
3862         
3863         if (this.menu) {
3864             this.menu.parentType = this.xtype;
3865             this.menu.triggerEl = this.el;
3866             this.menu = this.addxtype(Roo.apply({}, this.menu));
3867         }
3868         
3869     },
3870     onClick : function(e)
3871     {
3872         Roo.log('item on click ');
3873         
3874         if(this.preventDefault){
3875             e.preventDefault();
3876         }
3877         //this.parent().hideMenuItems();
3878         
3879         this.fireEvent('click', this, e);
3880     },
3881     getEl : function()
3882     {
3883         return this.el;
3884     } 
3885 });
3886
3887  
3888
3889  /*
3890  * - LGPL
3891  *
3892  * menu separator
3893  * 
3894  */
3895
3896
3897 /**
3898  * @class Roo.bootstrap.MenuSeparator
3899  * @extends Roo.bootstrap.Component
3900  * Bootstrap MenuSeparator class
3901  * 
3902  * @constructor
3903  * Create a new MenuItem
3904  * @param {Object} config The config object
3905  */
3906
3907
3908 Roo.bootstrap.MenuSeparator = function(config){
3909     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3910 };
3911
3912 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3913     
3914     getAutoCreate : function(){
3915         var cfg = {
3916             cls: 'divider',
3917             tag : 'li'
3918         };
3919         
3920         return cfg;
3921     }
3922    
3923 });
3924
3925  
3926
3927  
3928 /*
3929 * Licence: LGPL
3930 */
3931
3932 /**
3933  * @class Roo.bootstrap.Modal
3934  * @extends Roo.bootstrap.Component
3935  * Bootstrap Modal class
3936  * @cfg {String} title Title of dialog
3937  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3938  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3939  * @cfg {Boolean} specificTitle default false
3940  * @cfg {Array} buttons Array of buttons or standard button set..
3941  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3942  * @cfg {Boolean} animate default true
3943  * @cfg {Boolean} allow_close default true
3944  * @cfg {Boolean} fitwindow default false
3945  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3946  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3947  * @cfg {String} size (sm|lg|xl) default empty
3948  * @cfg {Number} max_width set the max width of modal
3949  * @cfg {Boolean} editableTitle can the title be edited
3950
3951  *
3952  *
3953  * @constructor
3954  * Create a new Modal Dialog
3955  * @param {Object} config The config object
3956  */
3957
3958 Roo.bootstrap.Modal = function(config){
3959     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3960     this.addEvents({
3961         // raw events
3962         /**
3963          * @event btnclick
3964          * The raw btnclick event for the button
3965          * @param {Roo.EventObject} e
3966          */
3967         "btnclick" : true,
3968         /**
3969          * @event resize
3970          * Fire when dialog resize
3971          * @param {Roo.bootstrap.Modal} this
3972          * @param {Roo.EventObject} e
3973          */
3974         "resize" : true,
3975         /**
3976          * @event titlechanged
3977          * Fire when the editable title has been changed
3978          * @param {Roo.bootstrap.Modal} this
3979          * @param {Roo.EventObject} value
3980          */
3981         "titlechanged" : true 
3982         
3983     });
3984     this.buttons = this.buttons || [];
3985
3986     if (this.tmpl) {
3987         this.tmpl = Roo.factory(this.tmpl);
3988     }
3989
3990 };
3991
3992 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3993
3994     title : 'test dialog',
3995
3996     buttons : false,
3997
3998     // set on load...
3999
4000     html: false,
4001
4002     tmp: false,
4003
4004     specificTitle: false,
4005
4006     buttonPosition: 'right',
4007
4008     allow_close : true,
4009
4010     animate : true,
4011
4012     fitwindow: false,
4013     
4014      // private
4015     dialogEl: false,
4016     bodyEl:  false,
4017     footerEl:  false,
4018     titleEl:  false,
4019     closeEl:  false,
4020
4021     size: '',
4022     
4023     max_width: 0,
4024     
4025     max_height: 0,
4026     
4027     fit_content: false,
4028     editableTitle  : false,
4029
4030     onRender : function(ct, position)
4031     {
4032         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4033
4034         if(!this.el){
4035             var cfg = Roo.apply({},  this.getAutoCreate());
4036             cfg.id = Roo.id();
4037             //if(!cfg.name){
4038             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4039             //}
4040             //if (!cfg.name.length) {
4041             //    delete cfg.name;
4042            // }
4043             if (this.cls) {
4044                 cfg.cls += ' ' + this.cls;
4045             }
4046             if (this.style) {
4047                 cfg.style = this.style;
4048             }
4049             this.el = Roo.get(document.body).createChild(cfg, position);
4050         }
4051         //var type = this.el.dom.type;
4052
4053
4054         if(this.tabIndex !== undefined){
4055             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4056         }
4057
4058         this.dialogEl = this.el.select('.modal-dialog',true).first();
4059         this.bodyEl = this.el.select('.modal-body',true).first();
4060         this.closeEl = this.el.select('.modal-header .close', true).first();
4061         this.headerEl = this.el.select('.modal-header',true).first();
4062         this.titleEl = this.el.select('.modal-title',true).first();
4063         this.footerEl = this.el.select('.modal-footer',true).first();
4064
4065         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4066         
4067         //this.el.addClass("x-dlg-modal");
4068
4069         if (this.buttons.length) {
4070             Roo.each(this.buttons, function(bb) {
4071                 var b = Roo.apply({}, bb);
4072                 b.xns = b.xns || Roo.bootstrap;
4073                 b.xtype = b.xtype || 'Button';
4074                 if (typeof(b.listeners) == 'undefined') {
4075                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4076                 }
4077
4078                 var btn = Roo.factory(b);
4079
4080                 btn.render(this.getButtonContainer());
4081
4082             },this);
4083         }
4084         // render the children.
4085         var nitems = [];
4086
4087         if(typeof(this.items) != 'undefined'){
4088             var items = this.items;
4089             delete this.items;
4090
4091             for(var i =0;i < items.length;i++) {
4092                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4093             }
4094         }
4095
4096         this.items = nitems;
4097
4098         // where are these used - they used to be body/close/footer
4099
4100
4101         this.initEvents();
4102         //this.el.addClass([this.fieldClass, this.cls]);
4103
4104     },
4105
4106     getAutoCreate : function()
4107     {
4108         // we will default to modal-body-overflow - might need to remove or make optional later.
4109         var bdy = {
4110                 cls : 'modal-body enable-modal-body-overflow ', 
4111                 html : this.html || ''
4112         };
4113
4114         var title = {
4115             tag: 'h4',
4116             cls : 'modal-title',
4117             html : this.title
4118         };
4119
4120         if(this.specificTitle){ // WTF is this?
4121             title = this.title;
4122         }
4123
4124         var header = [];
4125         if (this.allow_close && Roo.bootstrap.version == 3) {
4126             header.push({
4127                 tag: 'button',
4128                 cls : 'close',
4129                 html : '&times'
4130             });
4131         }
4132
4133         header.push(title);
4134
4135         if (this.editableTitle) {
4136             header.push({
4137                 cls: 'form-control roo-editable-title d-none',
4138                 tag: 'input',
4139                 type: 'text'
4140             });
4141         }
4142         
4143         if (this.allow_close && Roo.bootstrap.version == 4) {
4144             header.push({
4145                 tag: 'button',
4146                 cls : 'close',
4147                 html : '&times'
4148             });
4149         }
4150         
4151         var size = '';
4152
4153         if(this.size.length){
4154             size = 'modal-' + this.size;
4155         }
4156         
4157         var footer = Roo.bootstrap.version == 3 ?
4158             {
4159                 cls : 'modal-footer',
4160                 cn : [
4161                     {
4162                         tag: 'div',
4163                         cls: 'btn-' + this.buttonPosition
4164                     }
4165                 ]
4166
4167             } :
4168             {  // BS4 uses mr-auto on left buttons....
4169                 cls : 'modal-footer'
4170             };
4171
4172             
4173
4174         
4175         
4176         var modal = {
4177             cls: "modal",
4178              cn : [
4179                 {
4180                     cls: "modal-dialog " + size,
4181                     cn : [
4182                         {
4183                             cls : "modal-content",
4184                             cn : [
4185                                 {
4186                                     cls : 'modal-header',
4187                                     cn : header
4188                                 },
4189                                 bdy,
4190                                 footer
4191                             ]
4192
4193                         }
4194                     ]
4195
4196                 }
4197             ]
4198         };
4199
4200         if(this.animate){
4201             modal.cls += ' fade';
4202         }
4203
4204         return modal;
4205
4206     },
4207     getChildContainer : function() {
4208
4209          return this.bodyEl;
4210
4211     },
4212     getButtonContainer : function() {
4213         
4214          return Roo.bootstrap.version == 4 ?
4215             this.el.select('.modal-footer',true).first()
4216             : this.el.select('.modal-footer div',true).first();
4217
4218     },
4219     initEvents : function()
4220     {
4221         if (this.allow_close) {
4222             this.closeEl.on('click', this.hide, this);
4223         }
4224         Roo.EventManager.onWindowResize(this.resize, this, true);
4225         if (this.editableTitle) {
4226             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4227             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4228             this.headerEditEl.on('keyup', function(e) {
4229                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4230                         this.toggleHeaderInput(false)
4231                     }
4232                 }, this);
4233             this.headerEditEl.on('blur', function(e) {
4234                 this.toggleHeaderInput(false)
4235             },this);
4236         }
4237
4238     },
4239   
4240
4241     resize : function()
4242     {
4243         this.maskEl.setSize(
4244             Roo.lib.Dom.getViewWidth(true),
4245             Roo.lib.Dom.getViewHeight(true)
4246         );
4247         
4248         if (this.fitwindow) {
4249             
4250            
4251             this.setSize(
4252                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4253                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4254             );
4255             return;
4256         }
4257         
4258         if(this.max_width !== 0) {
4259             
4260             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4261             
4262             if(this.height) {
4263                 this.setSize(w, this.height);
4264                 return;
4265             }
4266             
4267             if(this.max_height) {
4268                 this.setSize(w,Math.min(
4269                     this.max_height,
4270                     Roo.lib.Dom.getViewportHeight(true) - 60
4271                 ));
4272                 
4273                 return;
4274             }
4275             
4276             if(!this.fit_content) {
4277                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4278                 return;
4279             }
4280             
4281             this.setSize(w, Math.min(
4282                 60 +
4283                 this.headerEl.getHeight() + 
4284                 this.footerEl.getHeight() + 
4285                 this.getChildHeight(this.bodyEl.dom.childNodes),
4286                 Roo.lib.Dom.getViewportHeight(true) - 60)
4287             );
4288         }
4289         
4290     },
4291
4292     setSize : function(w,h)
4293     {
4294         if (!w && !h) {
4295             return;
4296         }
4297         
4298         this.resizeTo(w,h);
4299     },
4300
4301     show : function() {
4302
4303         if (!this.rendered) {
4304             this.render();
4305         }
4306         this.toggleHeaderInput(false);
4307         //this.el.setStyle('display', 'block');
4308         this.el.removeClass('hideing');
4309         this.el.dom.style.display='block';
4310         
4311         Roo.get(document.body).addClass('modal-open');
4312  
4313         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4314             
4315             (function(){
4316                 this.el.addClass('show');
4317                 this.el.addClass('in');
4318             }).defer(50, this);
4319         }else{
4320             this.el.addClass('show');
4321             this.el.addClass('in');
4322         }
4323
4324         // not sure how we can show data in here..
4325         //if (this.tmpl) {
4326         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4327         //}
4328
4329         Roo.get(document.body).addClass("x-body-masked");
4330         
4331         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4332         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4333         this.maskEl.dom.style.display = 'block';
4334         this.maskEl.addClass('show');
4335         
4336         
4337         this.resize();
4338         
4339         this.fireEvent('show', this);
4340
4341         // set zindex here - otherwise it appears to be ignored...
4342         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4343
4344         (function () {
4345             this.items.forEach( function(e) {
4346                 e.layout ? e.layout() : false;
4347
4348             });
4349         }).defer(100,this);
4350
4351     },
4352     hide : function()
4353     {
4354         if(this.fireEvent("beforehide", this) !== false){
4355             
4356             this.maskEl.removeClass('show');
4357             
4358             this.maskEl.dom.style.display = '';
4359             Roo.get(document.body).removeClass("x-body-masked");
4360             this.el.removeClass('in');
4361             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4362
4363             if(this.animate){ // why
4364                 this.el.addClass('hideing');
4365                 this.el.removeClass('show');
4366                 (function(){
4367                     if (!this.el.hasClass('hideing')) {
4368                         return; // it's been shown again...
4369                     }
4370                     
4371                     this.el.dom.style.display='';
4372
4373                     Roo.get(document.body).removeClass('modal-open');
4374                     this.el.removeClass('hideing');
4375                 }).defer(150,this);
4376                 
4377             }else{
4378                 this.el.removeClass('show');
4379                 this.el.dom.style.display='';
4380                 Roo.get(document.body).removeClass('modal-open');
4381
4382             }
4383             this.fireEvent('hide', this);
4384         }
4385     },
4386     isVisible : function()
4387     {
4388         
4389         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4390         
4391     },
4392
4393     addButton : function(str, cb)
4394     {
4395
4396
4397         var b = Roo.apply({}, { html : str } );
4398         b.xns = b.xns || Roo.bootstrap;
4399         b.xtype = b.xtype || 'Button';
4400         if (typeof(b.listeners) == 'undefined') {
4401             b.listeners = { click : cb.createDelegate(this)  };
4402         }
4403
4404         var btn = Roo.factory(b);
4405
4406         btn.render(this.getButtonContainer());
4407
4408         return btn;
4409
4410     },
4411
4412     setDefaultButton : function(btn)
4413     {
4414         //this.el.select('.modal-footer').()
4415     },
4416
4417     resizeTo: function(w,h)
4418     {
4419         this.dialogEl.setWidth(w);
4420         
4421         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4422
4423         this.bodyEl.setHeight(h - diff);
4424         
4425         this.fireEvent('resize', this);
4426     },
4427     
4428     setContentSize  : function(w, h)
4429     {
4430
4431     },
4432     onButtonClick: function(btn,e)
4433     {
4434         //Roo.log([a,b,c]);
4435         this.fireEvent('btnclick', btn.name, e);
4436     },
4437      /**
4438      * Set the title of the Dialog
4439      * @param {String} str new Title
4440      */
4441     setTitle: function(str) {
4442         this.titleEl.dom.innerHTML = str;
4443         this.title = str;
4444     },
4445     /**
4446      * Set the body of the Dialog
4447      * @param {String} str new Title
4448      */
4449     setBody: function(str) {
4450         this.bodyEl.dom.innerHTML = str;
4451     },
4452     /**
4453      * Set the body of the Dialog using the template
4454      * @param {Obj} data - apply this data to the template and replace the body contents.
4455      */
4456     applyBody: function(obj)
4457     {
4458         if (!this.tmpl) {
4459             Roo.log("Error - using apply Body without a template");
4460             //code
4461         }
4462         this.tmpl.overwrite(this.bodyEl, obj);
4463     },
4464     
4465     getChildHeight : function(child_nodes)
4466     {
4467         if(
4468             !child_nodes ||
4469             child_nodes.length == 0
4470         ) {
4471             return 0;
4472         }
4473         
4474         var child_height = 0;
4475         
4476         for(var i = 0; i < child_nodes.length; i++) {
4477             
4478             /*
4479             * for modal with tabs...
4480             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4481                 
4482                 var layout_childs = child_nodes[i].childNodes;
4483                 
4484                 for(var j = 0; j < layout_childs.length; j++) {
4485                     
4486                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4487                         
4488                         var layout_body_childs = layout_childs[j].childNodes;
4489                         
4490                         for(var k = 0; k < layout_body_childs.length; k++) {
4491                             
4492                             if(layout_body_childs[k].classList.contains('navbar')) {
4493                                 child_height += layout_body_childs[k].offsetHeight;
4494                                 continue;
4495                             }
4496                             
4497                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4498                                 
4499                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4500                                 
4501                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4502                                     
4503                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4504                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4505                                         continue;
4506                                     }
4507                                     
4508                                 }
4509                                 
4510                             }
4511                             
4512                         }
4513                     }
4514                 }
4515                 continue;
4516             }
4517             */
4518             
4519             child_height += child_nodes[i].offsetHeight;
4520             // Roo.log(child_nodes[i].offsetHeight);
4521         }
4522         
4523         return child_height;
4524     },
4525     toggleHeaderInput : function(is_edit)
4526     {
4527         if (!this.editableTitle) {
4528             return; // not editable.
4529         }
4530         if (is_edit && this.is_header_editing) {
4531             return; // already editing..
4532         }
4533         if (is_edit) {
4534     
4535             this.headerEditEl.dom.value = this.title;
4536             this.headerEditEl.removeClass('d-none');
4537             this.headerEditEl.dom.focus();
4538             this.titleEl.addClass('d-none');
4539             
4540             this.is_header_editing = true;
4541             return
4542         }
4543         // flip back to not editing.
4544         this.title = this.headerEditEl.dom.value;
4545         this.headerEditEl.addClass('d-none');
4546         this.titleEl.removeClass('d-none');
4547         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4548         this.is_header_editing = false;
4549         this.fireEvent('titlechanged', this, this.title);
4550     
4551             
4552         
4553     }
4554
4555 });
4556
4557
4558 Roo.apply(Roo.bootstrap.Modal,  {
4559     /**
4560          * Button config that displays a single OK button
4561          * @type Object
4562          */
4563         OK :  [{
4564             name : 'ok',
4565             weight : 'primary',
4566             html : 'OK'
4567         }],
4568         /**
4569          * Button config that displays Yes and No buttons
4570          * @type Object
4571          */
4572         YESNO : [
4573             {
4574                 name  : 'no',
4575                 html : 'No'
4576             },
4577             {
4578                 name  :'yes',
4579                 weight : 'primary',
4580                 html : 'Yes'
4581             }
4582         ],
4583
4584         /**
4585          * Button config that displays OK and Cancel buttons
4586          * @type Object
4587          */
4588         OKCANCEL : [
4589             {
4590                name : 'cancel',
4591                 html : 'Cancel'
4592             },
4593             {
4594                 name : 'ok',
4595                 weight : 'primary',
4596                 html : 'OK'
4597             }
4598         ],
4599         /**
4600          * Button config that displays Yes, No and Cancel buttons
4601          * @type Object
4602          */
4603         YESNOCANCEL : [
4604             {
4605                 name : 'yes',
4606                 weight : 'primary',
4607                 html : 'Yes'
4608             },
4609             {
4610                 name : 'no',
4611                 html : 'No'
4612             },
4613             {
4614                 name : 'cancel',
4615                 html : 'Cancel'
4616             }
4617         ],
4618         
4619         zIndex : 10001
4620 });
4621
4622 /*
4623  * - LGPL
4624  *
4625  * messagebox - can be used as a replace
4626  * 
4627  */
4628 /**
4629  * @class Roo.MessageBox
4630  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4631  * Example usage:
4632  *<pre><code>
4633 // Basic alert:
4634 Roo.Msg.alert('Status', 'Changes saved successfully.');
4635
4636 // Prompt for user data:
4637 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4638     if (btn == 'ok'){
4639         // process text value...
4640     }
4641 });
4642
4643 // Show a dialog using config options:
4644 Roo.Msg.show({
4645    title:'Save Changes?',
4646    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4647    buttons: Roo.Msg.YESNOCANCEL,
4648    fn: processResult,
4649    animEl: 'elId'
4650 });
4651 </code></pre>
4652  * @singleton
4653  */
4654 Roo.bootstrap.MessageBox = function(){
4655     var dlg, opt, mask, waitTimer;
4656     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4657     var buttons, activeTextEl, bwidth;
4658
4659     
4660     // private
4661     var handleButton = function(button){
4662         dlg.hide();
4663         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4664     };
4665
4666     // private
4667     var handleHide = function(){
4668         if(opt && opt.cls){
4669             dlg.el.removeClass(opt.cls);
4670         }
4671         //if(waitTimer){
4672         //    Roo.TaskMgr.stop(waitTimer);
4673         //    waitTimer = null;
4674         //}
4675     };
4676
4677     // private
4678     var updateButtons = function(b){
4679         var width = 0;
4680         if(!b){
4681             buttons["ok"].hide();
4682             buttons["cancel"].hide();
4683             buttons["yes"].hide();
4684             buttons["no"].hide();
4685             dlg.footerEl.hide();
4686             
4687             return width;
4688         }
4689         dlg.footerEl.show();
4690         for(var k in buttons){
4691             if(typeof buttons[k] != "function"){
4692                 if(b[k]){
4693                     buttons[k].show();
4694                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4695                     width += buttons[k].el.getWidth()+15;
4696                 }else{
4697                     buttons[k].hide();
4698                 }
4699             }
4700         }
4701         return width;
4702     };
4703
4704     // private
4705     var handleEsc = function(d, k, e){
4706         if(opt && opt.closable !== false){
4707             dlg.hide();
4708         }
4709         if(e){
4710             e.stopEvent();
4711         }
4712     };
4713
4714     return {
4715         /**
4716          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4717          * @return {Roo.BasicDialog} The BasicDialog element
4718          */
4719         getDialog : function(){
4720            if(!dlg){
4721                 dlg = new Roo.bootstrap.Modal( {
4722                     //draggable: true,
4723                     //resizable:false,
4724                     //constraintoviewport:false,
4725                     //fixedcenter:true,
4726                     //collapsible : false,
4727                     //shim:true,
4728                     //modal: true,
4729                 //    width: 'auto',
4730                   //  height:100,
4731                     //buttonAlign:"center",
4732                     closeClick : function(){
4733                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4734                             handleButton("no");
4735                         }else{
4736                             handleButton("cancel");
4737                         }
4738                     }
4739                 });
4740                 dlg.render();
4741                 dlg.on("hide", handleHide);
4742                 mask = dlg.mask;
4743                 //dlg.addKeyListener(27, handleEsc);
4744                 buttons = {};
4745                 this.buttons = buttons;
4746                 var bt = this.buttonText;
4747                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4748                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4749                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4750                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4751                 //Roo.log(buttons);
4752                 bodyEl = dlg.bodyEl.createChild({
4753
4754                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4755                         '<textarea class="roo-mb-textarea"></textarea>' +
4756                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4757                 });
4758                 msgEl = bodyEl.dom.firstChild;
4759                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4760                 textboxEl.enableDisplayMode();
4761                 textboxEl.addKeyListener([10,13], function(){
4762                     if(dlg.isVisible() && opt && opt.buttons){
4763                         if(opt.buttons.ok){
4764                             handleButton("ok");
4765                         }else if(opt.buttons.yes){
4766                             handleButton("yes");
4767                         }
4768                     }
4769                 });
4770                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4771                 textareaEl.enableDisplayMode();
4772                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4773                 progressEl.enableDisplayMode();
4774                 
4775                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4776                 var pf = progressEl.dom.firstChild;
4777                 if (pf) {
4778                     pp = Roo.get(pf.firstChild);
4779                     pp.setHeight(pf.offsetHeight);
4780                 }
4781                 
4782             }
4783             return dlg;
4784         },
4785
4786         /**
4787          * Updates the message box body text
4788          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4789          * the XHTML-compliant non-breaking space character '&amp;#160;')
4790          * @return {Roo.MessageBox} This message box
4791          */
4792         updateText : function(text)
4793         {
4794             if(!dlg.isVisible() && !opt.width){
4795                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4796                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4797             }
4798             msgEl.innerHTML = text || '&#160;';
4799       
4800             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4801             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4802             var w = Math.max(
4803                     Math.min(opt.width || cw , this.maxWidth), 
4804                     Math.max(opt.minWidth || this.minWidth, bwidth)
4805             );
4806             if(opt.prompt){
4807                 activeTextEl.setWidth(w);
4808             }
4809             if(dlg.isVisible()){
4810                 dlg.fixedcenter = false;
4811             }
4812             // to big, make it scroll. = But as usual stupid IE does not support
4813             // !important..
4814             
4815             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4816                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4817                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4818             } else {
4819                 bodyEl.dom.style.height = '';
4820                 bodyEl.dom.style.overflowY = '';
4821             }
4822             if (cw > w) {
4823                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.overflowX = '';
4826             }
4827             
4828             dlg.setContentSize(w, bodyEl.getHeight());
4829             if(dlg.isVisible()){
4830                 dlg.fixedcenter = true;
4831             }
4832             return this;
4833         },
4834
4835         /**
4836          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4837          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4838          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4839          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4840          * @return {Roo.MessageBox} This message box
4841          */
4842         updateProgress : function(value, text){
4843             if(text){
4844                 this.updateText(text);
4845             }
4846             
4847             if (pp) { // weird bug on my firefox - for some reason this is not defined
4848                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4849                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4850             }
4851             return this;
4852         },        
4853
4854         /**
4855          * Returns true if the message box is currently displayed
4856          * @return {Boolean} True if the message box is visible, else false
4857          */
4858         isVisible : function(){
4859             return dlg && dlg.isVisible();  
4860         },
4861
4862         /**
4863          * Hides the message box if it is displayed
4864          */
4865         hide : function(){
4866             if(this.isVisible()){
4867                 dlg.hide();
4868             }  
4869         },
4870
4871         /**
4872          * Displays a new message box, or reinitializes an existing message box, based on the config options
4873          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4874          * The following config object properties are supported:
4875          * <pre>
4876 Property    Type             Description
4877 ----------  ---------------  ------------------------------------------------------------------------------------
4878 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4879                                    closes (defaults to undefined)
4880 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4881                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4882 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4883                                    progress and wait dialogs will ignore this property and always hide the
4884                                    close button as they can only be closed programmatically.
4885 cls               String           A custom CSS class to apply to the message box element
4886 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4887                                    displayed (defaults to 75)
4888 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4889                                    function will be btn (the name of the button that was clicked, if applicable,
4890                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4891                                    Progress and wait dialogs will ignore this option since they do not respond to
4892                                    user actions and can only be closed programmatically, so any required function
4893                                    should be called by the same code after it closes the dialog.
4894 icon              String           A CSS class that provides a background image to be used as an icon for
4895                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4896 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4897 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4898 modal             Boolean          False to allow user interaction with the page while the message box is
4899                                    displayed (defaults to true)
4900 msg               String           A string that will replace the existing message box body text (defaults
4901                                    to the XHTML-compliant non-breaking space character '&#160;')
4902 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4903 progress          Boolean          True to display a progress bar (defaults to false)
4904 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4905 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4906 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4907 title             String           The title text
4908 value             String           The string value to set into the active textbox element if displayed
4909 wait              Boolean          True to display a progress bar (defaults to false)
4910 width             Number           The width of the dialog in pixels
4911 </pre>
4912          *
4913          * Example usage:
4914          * <pre><code>
4915 Roo.Msg.show({
4916    title: 'Address',
4917    msg: 'Please enter your address:',
4918    width: 300,
4919    buttons: Roo.MessageBox.OKCANCEL,
4920    multiline: true,
4921    fn: saveAddress,
4922    animEl: 'addAddressBtn'
4923 });
4924 </code></pre>
4925          * @param {Object} config Configuration options
4926          * @return {Roo.MessageBox} This message box
4927          */
4928         show : function(options)
4929         {
4930             
4931             // this causes nightmares if you show one dialog after another
4932             // especially on callbacks..
4933              
4934             if(this.isVisible()){
4935                 
4936                 this.hide();
4937                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4938                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4939                 Roo.log("New Dialog Message:" +  options.msg )
4940                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4941                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4942                 
4943             }
4944             var d = this.getDialog();
4945             opt = options;
4946             d.setTitle(opt.title || "&#160;");
4947             d.closeEl.setDisplayed(opt.closable !== false);
4948             activeTextEl = textboxEl;
4949             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4950             if(opt.prompt){
4951                 if(opt.multiline){
4952                     textboxEl.hide();
4953                     textareaEl.show();
4954                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4955                         opt.multiline : this.defaultTextHeight);
4956                     activeTextEl = textareaEl;
4957                 }else{
4958                     textboxEl.show();
4959                     textareaEl.hide();
4960                 }
4961             }else{
4962                 textboxEl.hide();
4963                 textareaEl.hide();
4964             }
4965             progressEl.setDisplayed(opt.progress === true);
4966             if (opt.progress) {
4967                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4968             }
4969             this.updateProgress(0);
4970             activeTextEl.dom.value = opt.value || "";
4971             if(opt.prompt){
4972                 dlg.setDefaultButton(activeTextEl);
4973             }else{
4974                 var bs = opt.buttons;
4975                 var db = null;
4976                 if(bs && bs.ok){
4977                     db = buttons["ok"];
4978                 }else if(bs && bs.yes){
4979                     db = buttons["yes"];
4980                 }
4981                 dlg.setDefaultButton(db);
4982             }
4983             bwidth = updateButtons(opt.buttons);
4984             this.updateText(opt.msg);
4985             if(opt.cls){
4986                 d.el.addClass(opt.cls);
4987             }
4988             d.proxyDrag = opt.proxyDrag === true;
4989             d.modal = opt.modal !== false;
4990             d.mask = opt.modal !== false ? mask : false;
4991             if(!d.isVisible()){
4992                 // force it to the end of the z-index stack so it gets a cursor in FF
4993                 document.body.appendChild(dlg.el.dom);
4994                 d.animateTarget = null;
4995                 d.show(options.animEl);
4996             }
4997             return this;
4998         },
4999
5000         /**
5001          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5002          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5003          * and closing the message box when the process is complete.
5004          * @param {String} title The title bar text
5005          * @param {String} msg The message box body text
5006          * @return {Roo.MessageBox} This message box
5007          */
5008         progress : function(title, msg){
5009             this.show({
5010                 title : title,
5011                 msg : msg,
5012                 buttons: false,
5013                 progress:true,
5014                 closable:false,
5015                 minWidth: this.minProgressWidth,
5016                 modal : true
5017             });
5018             return this;
5019         },
5020
5021         /**
5022          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5023          * If a callback function is passed it will be called after the user clicks the button, and the
5024          * id of the button that was clicked will be passed as the only parameter to the callback
5025          * (could also be the top-right close button).
5026          * @param {String} title The title bar text
5027          * @param {String} msg The message box body text
5028          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5029          * @param {Object} scope (optional) The scope of the callback function
5030          * @return {Roo.MessageBox} This message box
5031          */
5032         alert : function(title, msg, fn, scope)
5033         {
5034             this.show({
5035                 title : title,
5036                 msg : msg,
5037                 buttons: this.OK,
5038                 fn: fn,
5039                 closable : false,
5040                 scope : scope,
5041                 modal : true
5042             });
5043             return this;
5044         },
5045
5046         /**
5047          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5048          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5049          * You are responsible for closing the message box when the process is complete.
5050          * @param {String} msg The message box body text
5051          * @param {String} title (optional) The title bar text
5052          * @return {Roo.MessageBox} This message box
5053          */
5054         wait : function(msg, title){
5055             this.show({
5056                 title : title,
5057                 msg : msg,
5058                 buttons: false,
5059                 closable:false,
5060                 progress:true,
5061                 modal:true,
5062                 width:300,
5063                 wait:true
5064             });
5065             waitTimer = Roo.TaskMgr.start({
5066                 run: function(i){
5067                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5068                 },
5069                 interval: 1000
5070             });
5071             return this;
5072         },
5073
5074         /**
5075          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5076          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5077          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5078          * @param {String} title The title bar text
5079          * @param {String} msg The message box body text
5080          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5081          * @param {Object} scope (optional) The scope of the callback function
5082          * @return {Roo.MessageBox} This message box
5083          */
5084         confirm : function(title, msg, fn, scope){
5085             this.show({
5086                 title : title,
5087                 msg : msg,
5088                 buttons: this.YESNO,
5089                 fn: fn,
5090                 scope : scope,
5091                 modal : true
5092             });
5093             return this;
5094         },
5095
5096         /**
5097          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5098          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5099          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5100          * (could also be the top-right close button) and the text that was entered will be passed as the two
5101          * parameters to the callback.
5102          * @param {String} title The title bar text
5103          * @param {String} msg The message box body text
5104          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5105          * @param {Object} scope (optional) The scope of the callback function
5106          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5107          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5108          * @return {Roo.MessageBox} This message box
5109          */
5110         prompt : function(title, msg, fn, scope, multiline){
5111             this.show({
5112                 title : title,
5113                 msg : msg,
5114                 buttons: this.OKCANCEL,
5115                 fn: fn,
5116                 minWidth:250,
5117                 scope : scope,
5118                 prompt:true,
5119                 multiline: multiline,
5120                 modal : true
5121             });
5122             return this;
5123         },
5124
5125         /**
5126          * Button config that displays a single OK button
5127          * @type Object
5128          */
5129         OK : {ok:true},
5130         /**
5131          * Button config that displays Yes and No buttons
5132          * @type Object
5133          */
5134         YESNO : {yes:true, no:true},
5135         /**
5136          * Button config that displays OK and Cancel buttons
5137          * @type Object
5138          */
5139         OKCANCEL : {ok:true, cancel:true},
5140         /**
5141          * Button config that displays Yes, No and Cancel buttons
5142          * @type Object
5143          */
5144         YESNOCANCEL : {yes:true, no:true, cancel:true},
5145
5146         /**
5147          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5148          * @type Number
5149          */
5150         defaultTextHeight : 75,
5151         /**
5152          * The maximum width in pixels of the message box (defaults to 600)
5153          * @type Number
5154          */
5155         maxWidth : 600,
5156         /**
5157          * The minimum width in pixels of the message box (defaults to 100)
5158          * @type Number
5159          */
5160         minWidth : 100,
5161         /**
5162          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5163          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5164          * @type Number
5165          */
5166         minProgressWidth : 250,
5167         /**
5168          * An object containing the default button text strings that can be overriden for localized language support.
5169          * Supported properties are: ok, cancel, yes and no.
5170          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5171          * @type Object
5172          */
5173         buttonText : {
5174             ok : "OK",
5175             cancel : "Cancel",
5176             yes : "Yes",
5177             no : "No"
5178         }
5179     };
5180 }();
5181
5182 /**
5183  * Shorthand for {@link Roo.MessageBox}
5184  */
5185 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5186 Roo.Msg = Roo.Msg || Roo.MessageBox;
5187 /*
5188  * - LGPL
5189  *
5190  * navbar
5191  * 
5192  */
5193
5194 /**
5195  * @class Roo.bootstrap.Navbar
5196  * @extends Roo.bootstrap.Component
5197  * Bootstrap Navbar class
5198
5199  * @constructor
5200  * Create a new Navbar
5201  * @param {Object} config The config object
5202  */
5203
5204
5205 Roo.bootstrap.Navbar = function(config){
5206     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5207     this.addEvents({
5208         // raw events
5209         /**
5210          * @event beforetoggle
5211          * Fire before toggle the menu
5212          * @param {Roo.EventObject} e
5213          */
5214         "beforetoggle" : true
5215     });
5216 };
5217
5218 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5219     
5220     
5221    
5222     // private
5223     navItems : false,
5224     loadMask : false,
5225     
5226     
5227     getAutoCreate : function(){
5228         
5229         
5230         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5231         
5232     },
5233     
5234     initEvents :function ()
5235     {
5236         //Roo.log(this.el.select('.navbar-toggle',true));
5237         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5238         
5239         var mark = {
5240             tag: "div",
5241             cls:"x-dlg-mask"
5242         };
5243         
5244         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5245         
5246         var size = this.el.getSize();
5247         this.maskEl.setSize(size.width, size.height);
5248         this.maskEl.enableDisplayMode("block");
5249         this.maskEl.hide();
5250         
5251         if(this.loadMask){
5252             this.maskEl.show();
5253         }
5254     },
5255     
5256     
5257     getChildContainer : function()
5258     {
5259         if (this.el && this.el.select('.collapse').getCount()) {
5260             return this.el.select('.collapse',true).first();
5261         }
5262         
5263         return this.el;
5264     },
5265     
5266     mask : function()
5267     {
5268         this.maskEl.show();
5269     },
5270     
5271     unmask : function()
5272     {
5273         this.maskEl.hide();
5274     },
5275     onToggle : function()
5276     {
5277         
5278         if(this.fireEvent('beforetoggle', this) === false){
5279             return;
5280         }
5281         var ce = this.el.select('.navbar-collapse',true).first();
5282       
5283         if (!ce.hasClass('show')) {
5284            this.expand();
5285         } else {
5286             this.collapse();
5287         }
5288         
5289         
5290     
5291     },
5292     /**
5293      * Expand the navbar pulldown 
5294      */
5295     expand : function ()
5296     {
5297        
5298         var ce = this.el.select('.navbar-collapse',true).first();
5299         if (ce.hasClass('collapsing')) {
5300             return;
5301         }
5302         ce.dom.style.height = '';
5303                // show it...
5304         ce.addClass('in'); // old...
5305         ce.removeClass('collapse');
5306         ce.addClass('show');
5307         var h = ce.getHeight();
5308         Roo.log(h);
5309         ce.removeClass('show');
5310         // at this point we should be able to see it..
5311         ce.addClass('collapsing');
5312         
5313         ce.setHeight(0); // resize it ...
5314         ce.on('transitionend', function() {
5315             //Roo.log('done transition');
5316             ce.removeClass('collapsing');
5317             ce.addClass('show');
5318             ce.removeClass('collapse');
5319
5320             ce.dom.style.height = '';
5321         }, this, { single: true} );
5322         ce.setHeight(h);
5323         ce.dom.scrollTop = 0;
5324     },
5325     /**
5326      * Collapse the navbar pulldown 
5327      */
5328     collapse : function()
5329     {
5330          var ce = this.el.select('.navbar-collapse',true).first();
5331        
5332         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5333             // it's collapsed or collapsing..
5334             return;
5335         }
5336         ce.removeClass('in'); // old...
5337         ce.setHeight(ce.getHeight());
5338         ce.removeClass('show');
5339         ce.addClass('collapsing');
5340         
5341         ce.on('transitionend', function() {
5342             ce.dom.style.height = '';
5343             ce.removeClass('collapsing');
5344             ce.addClass('collapse');
5345         }, this, { single: true} );
5346         ce.setHeight(0);
5347     }
5348     
5349     
5350     
5351 });
5352
5353
5354
5355  
5356
5357  /*
5358  * - LGPL
5359  *
5360  * navbar
5361  * 
5362  */
5363
5364 /**
5365  * @class Roo.bootstrap.NavSimplebar
5366  * @extends Roo.bootstrap.Navbar
5367  * Bootstrap Sidebar class
5368  *
5369  * @cfg {Boolean} inverse is inverted color
5370  * 
5371  * @cfg {String} type (nav | pills | tabs)
5372  * @cfg {Boolean} arrangement stacked | justified
5373  * @cfg {String} align (left | right) alignment
5374  * 
5375  * @cfg {Boolean} main (true|false) main nav bar? default false
5376  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5377  * 
5378  * @cfg {String} tag (header|footer|nav|div) default is nav 
5379
5380  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5381  * 
5382  * 
5383  * @constructor
5384  * Create a new Sidebar
5385  * @param {Object} config The config object
5386  */
5387
5388
5389 Roo.bootstrap.NavSimplebar = function(config){
5390     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5391 };
5392
5393 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5394     
5395     inverse: false,
5396     
5397     type: false,
5398     arrangement: '',
5399     align : false,
5400     
5401     weight : 'light',
5402     
5403     main : false,
5404     
5405     
5406     tag : false,
5407     
5408     
5409     getAutoCreate : function(){
5410         
5411         
5412         var cfg = {
5413             tag : this.tag || 'div',
5414             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5415         };
5416         if (['light','white'].indexOf(this.weight) > -1) {
5417             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5418         }
5419         cfg.cls += ' bg-' + this.weight;
5420         
5421         if (this.inverse) {
5422             cfg.cls += ' navbar-inverse';
5423             
5424         }
5425         
5426         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5427         
5428         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5429             return cfg;
5430         }
5431         
5432         
5433     
5434         
5435         cfg.cn = [
5436             {
5437                 cls: 'nav nav-' + this.xtype,
5438                 tag : 'ul'
5439             }
5440         ];
5441         
5442          
5443         this.type = this.type || 'nav';
5444         if (['tabs','pills'].indexOf(this.type) != -1) {
5445             cfg.cn[0].cls += ' nav-' + this.type
5446         
5447         
5448         } else {
5449             if (this.type!=='nav') {
5450                 Roo.log('nav type must be nav/tabs/pills')
5451             }
5452             cfg.cn[0].cls += ' navbar-nav'
5453         }
5454         
5455         
5456         
5457         
5458         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5459             cfg.cn[0].cls += ' nav-' + this.arrangement;
5460         }
5461         
5462         
5463         if (this.align === 'right') {
5464             cfg.cn[0].cls += ' navbar-right';
5465         }
5466         
5467         
5468         
5469         
5470         return cfg;
5471     
5472         
5473     }
5474     
5475     
5476     
5477 });
5478
5479
5480
5481  
5482
5483  
5484        /*
5485  * - LGPL
5486  *
5487  * navbar
5488  * navbar-fixed-top
5489  * navbar-expand-md  fixed-top 
5490  */
5491
5492 /**
5493  * @class Roo.bootstrap.NavHeaderbar
5494  * @extends Roo.bootstrap.NavSimplebar
5495  * Bootstrap Sidebar class
5496  *
5497  * @cfg {String} brand what is brand
5498  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5499  * @cfg {String} brand_href href of the brand
5500  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5501  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5502  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5503  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5504  * 
5505  * @constructor
5506  * Create a new Sidebar
5507  * @param {Object} config The config object
5508  */
5509
5510
5511 Roo.bootstrap.NavHeaderbar = function(config){
5512     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5513       
5514 };
5515
5516 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5517     
5518     position: '',
5519     brand: '',
5520     brand_href: false,
5521     srButton : true,
5522     autohide : false,
5523     desktopCenter : false,
5524    
5525     
5526     getAutoCreate : function(){
5527         
5528         var   cfg = {
5529             tag: this.nav || 'nav',
5530             cls: 'navbar navbar-expand-md',
5531             role: 'navigation',
5532             cn: []
5533         };
5534         
5535         var cn = cfg.cn;
5536         if (this.desktopCenter) {
5537             cn.push({cls : 'container', cn : []});
5538             cn = cn[0].cn;
5539         }
5540         
5541         if(this.srButton){
5542             var btn = {
5543                 tag: 'button',
5544                 type: 'button',
5545                 cls: 'navbar-toggle navbar-toggler',
5546                 'data-toggle': 'collapse',
5547                 cn: [
5548                     {
5549                         tag: 'span',
5550                         cls: 'sr-only',
5551                         html: 'Toggle navigation'
5552                     },
5553                     {
5554                         tag: 'span',
5555                         cls: 'icon-bar navbar-toggler-icon'
5556                     },
5557                     {
5558                         tag: 'span',
5559                         cls: 'icon-bar'
5560                     },
5561                     {
5562                         tag: 'span',
5563                         cls: 'icon-bar'
5564                     }
5565                 ]
5566             };
5567             
5568             cn.push( Roo.bootstrap.version == 4 ? btn : {
5569                 tag: 'div',
5570                 cls: 'navbar-header',
5571                 cn: [
5572                     btn
5573                 ]
5574             });
5575         }
5576         
5577         cn.push({
5578             tag: 'div',
5579             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5580             cn : []
5581         });
5582         
5583         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5584         
5585         if (['light','white'].indexOf(this.weight) > -1) {
5586             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5587         }
5588         cfg.cls += ' bg-' + this.weight;
5589         
5590         
5591         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5592             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5593             
5594             // tag can override this..
5595             
5596             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5597         }
5598         
5599         if (this.brand !== '') {
5600             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5601             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5602                 tag: 'a',
5603                 href: this.brand_href ? this.brand_href : '#',
5604                 cls: 'navbar-brand',
5605                 cn: [
5606                 this.brand
5607                 ]
5608             });
5609         }
5610         
5611         if(this.main){
5612             cfg.cls += ' main-nav';
5613         }
5614         
5615         
5616         return cfg;
5617
5618         
5619     },
5620     getHeaderChildContainer : function()
5621     {
5622         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5623             return this.el.select('.navbar-header',true).first();
5624         }
5625         
5626         return this.getChildContainer();
5627     },
5628     
5629     getChildContainer : function()
5630     {
5631          
5632         return this.el.select('.roo-navbar-collapse',true).first();
5633          
5634         
5635     },
5636     
5637     initEvents : function()
5638     {
5639         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5640         
5641         if (this.autohide) {
5642             
5643             var prevScroll = 0;
5644             var ft = this.el;
5645             
5646             Roo.get(document).on('scroll',function(e) {
5647                 var ns = Roo.get(document).getScroll().top;
5648                 var os = prevScroll;
5649                 prevScroll = ns;
5650                 
5651                 if(ns > os){
5652                     ft.removeClass('slideDown');
5653                     ft.addClass('slideUp');
5654                     return;
5655                 }
5656                 ft.removeClass('slideUp');
5657                 ft.addClass('slideDown');
5658                  
5659               
5660           },this);
5661         }
5662     }    
5663     
5664 });
5665
5666
5667
5668  
5669
5670  /*
5671  * - LGPL
5672  *
5673  * navbar
5674  * 
5675  */
5676
5677 /**
5678  * @class Roo.bootstrap.NavSidebar
5679  * @extends Roo.bootstrap.Navbar
5680  * Bootstrap Sidebar class
5681  * 
5682  * @constructor
5683  * Create a new Sidebar
5684  * @param {Object} config The config object
5685  */
5686
5687
5688 Roo.bootstrap.NavSidebar = function(config){
5689     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5690 };
5691
5692 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5693     
5694     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5695     
5696     getAutoCreate : function(){
5697         
5698         
5699         return  {
5700             tag: 'div',
5701             cls: 'sidebar sidebar-nav'
5702         };
5703     
5704         
5705     }
5706     
5707     
5708     
5709 });
5710
5711
5712
5713  
5714
5715  /*
5716  * - LGPL
5717  *
5718  * nav group
5719  * 
5720  */
5721
5722 /**
5723  * @class Roo.bootstrap.NavGroup
5724  * @extends Roo.bootstrap.Component
5725  * Bootstrap NavGroup class
5726  * @cfg {String} align (left|right)
5727  * @cfg {Boolean} inverse
5728  * @cfg {String} type (nav|pills|tab) default nav
5729  * @cfg {String} navId - reference Id for navbar.
5730
5731  * 
5732  * @constructor
5733  * Create a new nav group
5734  * @param {Object} config The config object
5735  */
5736
5737 Roo.bootstrap.NavGroup = function(config){
5738     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5739     this.navItems = [];
5740    
5741     Roo.bootstrap.NavGroup.register(this);
5742      this.addEvents({
5743         /**
5744              * @event changed
5745              * Fires when the active item changes
5746              * @param {Roo.bootstrap.NavGroup} this
5747              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5748              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5749          */
5750         'changed': true
5751      });
5752     
5753 };
5754
5755 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5756     
5757     align: '',
5758     inverse: false,
5759     form: false,
5760     type: 'nav',
5761     navId : '',
5762     // private
5763     
5764     navItems : false, 
5765     
5766     getAutoCreate : function()
5767     {
5768         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5769         
5770         cfg = {
5771             tag : 'ul',
5772             cls: 'nav' 
5773         };
5774         if (Roo.bootstrap.version == 4) {
5775             if (['tabs','pills'].indexOf(this.type) != -1) {
5776                 cfg.cls += ' nav-' + this.type; 
5777             } else {
5778                 // trying to remove so header bar can right align top?
5779                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5780                     // do not use on header bar... 
5781                     cfg.cls += ' navbar-nav';
5782                 }
5783             }
5784             
5785         } else {
5786             if (['tabs','pills'].indexOf(this.type) != -1) {
5787                 cfg.cls += ' nav-' + this.type
5788             } else {
5789                 if (this.type !== 'nav') {
5790                     Roo.log('nav type must be nav/tabs/pills')
5791                 }
5792                 cfg.cls += ' navbar-nav'
5793             }
5794         }
5795         
5796         if (this.parent() && this.parent().sidebar) {
5797             cfg = {
5798                 tag: 'ul',
5799                 cls: 'dashboard-menu sidebar-menu'
5800             };
5801             
5802             return cfg;
5803         }
5804         
5805         if (this.form === true) {
5806             cfg = {
5807                 tag: 'form',
5808                 cls: 'navbar-form form-inline'
5809             };
5810             //nav navbar-right ml-md-auto
5811             if (this.align === 'right') {
5812                 cfg.cls += ' navbar-right ml-md-auto';
5813             } else {
5814                 cfg.cls += ' navbar-left';
5815             }
5816         }
5817         
5818         if (this.align === 'right') {
5819             cfg.cls += ' navbar-right ml-md-auto';
5820         } else {
5821             cfg.cls += ' mr-auto';
5822         }
5823         
5824         if (this.inverse) {
5825             cfg.cls += ' navbar-inverse';
5826             
5827         }
5828         
5829         
5830         return cfg;
5831     },
5832     /**
5833     * sets the active Navigation item
5834     * @param {Roo.bootstrap.NavItem} the new current navitem
5835     */
5836     setActiveItem : function(item)
5837     {
5838         var prev = false;
5839         Roo.each(this.navItems, function(v){
5840             if (v == item) {
5841                 return ;
5842             }
5843             if (v.isActive()) {
5844                 v.setActive(false, true);
5845                 prev = v;
5846                 
5847             }
5848             
5849         });
5850
5851         item.setActive(true, true);
5852         this.fireEvent('changed', this, item, prev);
5853         
5854         
5855     },
5856     /**
5857     * gets the active Navigation item
5858     * @return {Roo.bootstrap.NavItem} the current navitem
5859     */
5860     getActive : function()
5861     {
5862         
5863         var prev = false;
5864         Roo.each(this.navItems, function(v){
5865             
5866             if (v.isActive()) {
5867                 prev = v;
5868                 
5869             }
5870             
5871         });
5872         return prev;
5873     },
5874     
5875     indexOfNav : function()
5876     {
5877         
5878         var prev = false;
5879         Roo.each(this.navItems, function(v,i){
5880             
5881             if (v.isActive()) {
5882                 prev = i;
5883                 
5884             }
5885             
5886         });
5887         return prev;
5888     },
5889     /**
5890     * adds a Navigation item
5891     * @param {Roo.bootstrap.NavItem} the navitem to add
5892     */
5893     addItem : function(cfg)
5894     {
5895         if (this.form && Roo.bootstrap.version == 4) {
5896             cfg.tag = 'div';
5897         }
5898         var cn = new Roo.bootstrap.NavItem(cfg);
5899         this.register(cn);
5900         cn.parentId = this.id;
5901         cn.onRender(this.el, null);
5902         return cn;
5903     },
5904     /**
5905     * register a Navigation item
5906     * @param {Roo.bootstrap.NavItem} the navitem to add
5907     */
5908     register : function(item)
5909     {
5910         this.navItems.push( item);
5911         item.navId = this.navId;
5912     
5913     },
5914     
5915     /**
5916     * clear all the Navigation item
5917     */
5918    
5919     clearAll : function()
5920     {
5921         this.navItems = [];
5922         this.el.dom.innerHTML = '';
5923     },
5924     
5925     getNavItem: function(tabId)
5926     {
5927         var ret = false;
5928         Roo.each(this.navItems, function(e) {
5929             if (e.tabId == tabId) {
5930                ret =  e;
5931                return false;
5932             }
5933             return true;
5934             
5935         });
5936         return ret;
5937     },
5938     
5939     setActiveNext : function()
5940     {
5941         var i = this.indexOfNav(this.getActive());
5942         if (i > this.navItems.length) {
5943             return;
5944         }
5945         this.setActiveItem(this.navItems[i+1]);
5946     },
5947     setActivePrev : function()
5948     {
5949         var i = this.indexOfNav(this.getActive());
5950         if (i  < 1) {
5951             return;
5952         }
5953         this.setActiveItem(this.navItems[i-1]);
5954     },
5955     clearWasActive : function(except) {
5956         Roo.each(this.navItems, function(e) {
5957             if (e.tabId != except.tabId && e.was_active) {
5958                e.was_active = false;
5959                return false;
5960             }
5961             return true;
5962             
5963         });
5964     },
5965     getWasActive : function ()
5966     {
5967         var r = false;
5968         Roo.each(this.navItems, function(e) {
5969             if (e.was_active) {
5970                r = e;
5971                return false;
5972             }
5973             return true;
5974             
5975         });
5976         return r;
5977     }
5978     
5979     
5980 });
5981
5982  
5983 Roo.apply(Roo.bootstrap.NavGroup, {
5984     
5985     groups: {},
5986      /**
5987     * register a Navigation Group
5988     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5989     */
5990     register : function(navgrp)
5991     {
5992         this.groups[navgrp.navId] = navgrp;
5993         
5994     },
5995     /**
5996     * fetch a Navigation Group based on the navigation ID
5997     * @param {string} the navgroup to add
5998     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5999     */
6000     get: function(navId) {
6001         if (typeof(this.groups[navId]) == 'undefined') {
6002             return false;
6003             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6004         }
6005         return this.groups[navId] ;
6006     }
6007     
6008     
6009     
6010 });
6011
6012  /*
6013  * - LGPL
6014  *
6015  * row
6016  * 
6017  */
6018
6019 /**
6020  * @class Roo.bootstrap.NavItem
6021  * @extends Roo.bootstrap.Component
6022  * Bootstrap Navbar.NavItem class
6023  * @cfg {String} href  link to
6024  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6025
6026  * @cfg {String} html content of button
6027  * @cfg {String} badge text inside badge
6028  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6029  * @cfg {String} glyphicon DEPRICATED - use fa
6030  * @cfg {String} icon DEPRICATED - use fa
6031  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6032  * @cfg {Boolean} active Is item active
6033  * @cfg {Boolean} disabled Is item disabled
6034  
6035  * @cfg {Boolean} preventDefault (true | false) default false
6036  * @cfg {String} tabId the tab that this item activates.
6037  * @cfg {String} tagtype (a|span) render as a href or span?
6038  * @cfg {Boolean} animateRef (true|false) link to element default false  
6039   
6040  * @constructor
6041  * Create a new Navbar Item
6042  * @param {Object} config The config object
6043  */
6044 Roo.bootstrap.NavItem = function(config){
6045     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6046     this.addEvents({
6047         // raw events
6048         /**
6049          * @event click
6050          * The raw click event for the entire grid.
6051          * @param {Roo.EventObject} e
6052          */
6053         "click" : true,
6054          /**
6055             * @event changed
6056             * Fires when the active item active state changes
6057             * @param {Roo.bootstrap.NavItem} this
6058             * @param {boolean} state the new state
6059              
6060          */
6061         'changed': true,
6062         /**
6063             * @event scrollto
6064             * Fires when scroll to element
6065             * @param {Roo.bootstrap.NavItem} this
6066             * @param {Object} options
6067             * @param {Roo.EventObject} e
6068              
6069          */
6070         'scrollto': true
6071     });
6072    
6073 };
6074
6075 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6076     
6077     href: false,
6078     html: '',
6079     badge: '',
6080     icon: false,
6081     fa : false,
6082     glyphicon: false,
6083     active: false,
6084     preventDefault : false,
6085     tabId : false,
6086     tagtype : 'a',
6087     tag: 'li',
6088     disabled : false,
6089     animateRef : false,
6090     was_active : false,
6091     button_weight : '',
6092     button_outline : false,
6093     
6094     navLink: false,
6095     
6096     getAutoCreate : function(){
6097          
6098         var cfg = {
6099             tag: this.tag,
6100             cls: 'nav-item'
6101         };
6102         
6103         if (this.active) {
6104             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6105         }
6106         if (this.disabled) {
6107             cfg.cls += ' disabled';
6108         }
6109         
6110         // BS4 only?
6111         if (this.button_weight.length) {
6112             cfg.tag = this.href ? 'a' : 'button';
6113             cfg.html = this.html || '';
6114             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6115             if (this.href) {
6116                 cfg.href = this.href;
6117             }
6118             if (this.fa) {
6119                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6120             }
6121             
6122             // menu .. should add dropdown-menu class - so no need for carat..
6123             
6124             if (this.badge !== '') {
6125                  
6126                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6127             }
6128             return cfg;
6129         }
6130         
6131         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6132             cfg.cn = [
6133                 {
6134                     tag: this.tagtype,
6135                     href : this.href || "#",
6136                     html: this.html || ''
6137                 }
6138             ];
6139             if (this.tagtype == 'a') {
6140                 cfg.cn[0].cls = 'nav-link';
6141             }
6142             if (this.icon) {
6143                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6144             }
6145             if (this.fa) {
6146                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6147             }
6148             if(this.glyphicon) {
6149                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6150             }
6151             
6152             if (this.menu) {
6153                 
6154                 cfg.cn[0].html += " <span class='caret'></span>";
6155              
6156             }
6157             
6158             if (this.badge !== '') {
6159                  
6160                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6161             }
6162         }
6163         
6164         
6165         
6166         return cfg;
6167     },
6168     onRender : function(ct, position)
6169     {
6170        // Roo.log("Call onRender: " + this.xtype);
6171         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6172             this.tag = 'div';
6173         }
6174         
6175         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6176         this.navLink = this.el.select('.nav-link',true).first();
6177         return ret;
6178     },
6179       
6180     
6181     initEvents: function() 
6182     {
6183         if (typeof (this.menu) != 'undefined') {
6184             this.menu.parentType = this.xtype;
6185             this.menu.triggerEl = this.el;
6186             this.menu = this.addxtype(Roo.apply({}, this.menu));
6187         }
6188         
6189         this.el.select('a',true).on('click', this.onClick, this);
6190         
6191         if(this.tagtype == 'span'){
6192             this.el.select('span',true).on('click', this.onClick, this);
6193         }
6194        
6195         // at this point parent should be available..
6196         this.parent().register(this);
6197     },
6198     
6199     onClick : function(e)
6200     {
6201         if (e.getTarget('.dropdown-menu-item')) {
6202             // did you click on a menu itemm.... - then don't trigger onclick..
6203             return;
6204         }
6205         
6206         if(
6207                 this.preventDefault || 
6208                 this.href == '#' 
6209         ){
6210             Roo.log("NavItem - prevent Default?");
6211             e.preventDefault();
6212         }
6213         
6214         if (this.disabled) {
6215             return;
6216         }
6217         
6218         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6219         if (tg && tg.transition) {
6220             Roo.log("waiting for the transitionend");
6221             return;
6222         }
6223         
6224         
6225         
6226         //Roo.log("fire event clicked");
6227         if(this.fireEvent('click', this, e) === false){
6228             return;
6229         };
6230         
6231         if(this.tagtype == 'span'){
6232             return;
6233         }
6234         
6235         //Roo.log(this.href);
6236         var ael = this.el.select('a',true).first();
6237         //Roo.log(ael);
6238         
6239         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6240             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6241             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6242                 return; // ignore... - it's a 'hash' to another page.
6243             }
6244             Roo.log("NavItem - prevent Default?");
6245             e.preventDefault();
6246             this.scrollToElement(e);
6247         }
6248         
6249         
6250         var p =  this.parent();
6251    
6252         if (['tabs','pills'].indexOf(p.type)!==-1) {
6253             if (typeof(p.setActiveItem) !== 'undefined') {
6254                 p.setActiveItem(this);
6255             }
6256         }
6257         
6258         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6259         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6260             // remove the collapsed menu expand...
6261             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6262         }
6263     },
6264     
6265     isActive: function () {
6266         return this.active
6267     },
6268     setActive : function(state, fire, is_was_active)
6269     {
6270         if (this.active && !state && this.navId) {
6271             this.was_active = true;
6272             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6273             if (nv) {
6274                 nv.clearWasActive(this);
6275             }
6276             
6277         }
6278         this.active = state;
6279         
6280         if (!state ) {
6281             this.el.removeClass('active');
6282             this.navLink ? this.navLink.removeClass('active') : false;
6283         } else if (!this.el.hasClass('active')) {
6284             
6285             this.el.addClass('active');
6286             if (Roo.bootstrap.version == 4 && this.navLink ) {
6287                 this.navLink.addClass('active');
6288             }
6289             
6290         }
6291         if (fire) {
6292             this.fireEvent('changed', this, state);
6293         }
6294         
6295         // show a panel if it's registered and related..
6296         
6297         if (!this.navId || !this.tabId || !state || is_was_active) {
6298             return;
6299         }
6300         
6301         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6302         if (!tg) {
6303             return;
6304         }
6305         var pan = tg.getPanelByName(this.tabId);
6306         if (!pan) {
6307             return;
6308         }
6309         // if we can not flip to new panel - go back to old nav highlight..
6310         if (false == tg.showPanel(pan)) {
6311             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6312             if (nv) {
6313                 var onav = nv.getWasActive();
6314                 if (onav) {
6315                     onav.setActive(true, false, true);
6316                 }
6317             }
6318             
6319         }
6320         
6321         
6322         
6323     },
6324      // this should not be here...
6325     setDisabled : function(state)
6326     {
6327         this.disabled = state;
6328         if (!state ) {
6329             this.el.removeClass('disabled');
6330         } else if (!this.el.hasClass('disabled')) {
6331             this.el.addClass('disabled');
6332         }
6333         
6334     },
6335     
6336     /**
6337      * Fetch the element to display the tooltip on.
6338      * @return {Roo.Element} defaults to this.el
6339      */
6340     tooltipEl : function()
6341     {
6342         return this.el.select('' + this.tagtype + '', true).first();
6343     },
6344     
6345     scrollToElement : function(e)
6346     {
6347         var c = document.body;
6348         
6349         /*
6350          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6351          */
6352         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6353             c = document.documentElement;
6354         }
6355         
6356         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6357         
6358         if(!target){
6359             return;
6360         }
6361
6362         var o = target.calcOffsetsTo(c);
6363         
6364         var options = {
6365             target : target,
6366             value : o[1]
6367         };
6368         
6369         this.fireEvent('scrollto', this, options, e);
6370         
6371         Roo.get(c).scrollTo('top', options.value, true);
6372         
6373         return;
6374     }
6375 });
6376  
6377
6378  /*
6379  * - LGPL
6380  *
6381  * sidebar item
6382  *
6383  *  li
6384  *    <span> icon </span>
6385  *    <span> text </span>
6386  *    <span>badge </span>
6387  */
6388
6389 /**
6390  * @class Roo.bootstrap.NavSidebarItem
6391  * @extends Roo.bootstrap.NavItem
6392  * Bootstrap Navbar.NavSidebarItem class
6393  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6394  * {Boolean} open is the menu open
6395  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6396  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6397  * {String} buttonSize (sm|md|lg)the extra classes for the button
6398  * {Boolean} showArrow show arrow next to the text (default true)
6399  * @constructor
6400  * Create a new Navbar Button
6401  * @param {Object} config The config object
6402  */
6403 Roo.bootstrap.NavSidebarItem = function(config){
6404     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6405     this.addEvents({
6406         // raw events
6407         /**
6408          * @event click
6409          * The raw click event for the entire grid.
6410          * @param {Roo.EventObject} e
6411          */
6412         "click" : true,
6413          /**
6414             * @event changed
6415             * Fires when the active item active state changes
6416             * @param {Roo.bootstrap.NavSidebarItem} this
6417             * @param {boolean} state the new state
6418              
6419          */
6420         'changed': true
6421     });
6422    
6423 };
6424
6425 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6426     
6427     badgeWeight : 'default',
6428     
6429     open: false,
6430     
6431     buttonView : false,
6432     
6433     buttonWeight : 'default',
6434     
6435     buttonSize : 'md',
6436     
6437     showArrow : true,
6438     
6439     getAutoCreate : function(){
6440         
6441         
6442         var a = {
6443                 tag: 'a',
6444                 href : this.href || '#',
6445                 cls: '',
6446                 html : '',
6447                 cn : []
6448         };
6449         
6450         if(this.buttonView){
6451             a = {
6452                 tag: 'button',
6453                 href : this.href || '#',
6454                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6455                 html : this.html,
6456                 cn : []
6457             };
6458         }
6459         
6460         var cfg = {
6461             tag: 'li',
6462             cls: '',
6463             cn: [ a ]
6464         };
6465         
6466         if (this.active) {
6467             cfg.cls += ' active';
6468         }
6469         
6470         if (this.disabled) {
6471             cfg.cls += ' disabled';
6472         }
6473         if (this.open) {
6474             cfg.cls += ' open x-open';
6475         }
6476         // left icon..
6477         if (this.glyphicon || this.icon) {
6478             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6479             a.cn.push({ tag : 'i', cls : c }) ;
6480         }
6481         
6482         if(!this.buttonView){
6483             var span = {
6484                 tag: 'span',
6485                 html : this.html || ''
6486             };
6487
6488             a.cn.push(span);
6489             
6490         }
6491         
6492         if (this.badge !== '') {
6493             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6494         }
6495         
6496         if (this.menu) {
6497             
6498             if(this.showArrow){
6499                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6500             }
6501             
6502             a.cls += ' dropdown-toggle treeview' ;
6503         }
6504         
6505         return cfg;
6506     },
6507     
6508     initEvents : function()
6509     { 
6510         if (typeof (this.menu) != 'undefined') {
6511             this.menu.parentType = this.xtype;
6512             this.menu.triggerEl = this.el;
6513             this.menu = this.addxtype(Roo.apply({}, this.menu));
6514         }
6515         
6516         this.el.on('click', this.onClick, this);
6517         
6518         if(this.badge !== ''){
6519             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6520         }
6521         
6522     },
6523     
6524     onClick : function(e)
6525     {
6526         if(this.disabled){
6527             e.preventDefault();
6528             return;
6529         }
6530         
6531         if(this.preventDefault){
6532             e.preventDefault();
6533         }
6534         
6535         this.fireEvent('click', this, e);
6536     },
6537     
6538     disable : function()
6539     {
6540         this.setDisabled(true);
6541     },
6542     
6543     enable : function()
6544     {
6545         this.setDisabled(false);
6546     },
6547     
6548     setDisabled : function(state)
6549     {
6550         if(this.disabled == state){
6551             return;
6552         }
6553         
6554         this.disabled = state;
6555         
6556         if (state) {
6557             this.el.addClass('disabled');
6558             return;
6559         }
6560         
6561         this.el.removeClass('disabled');
6562         
6563         return;
6564     },
6565     
6566     setActive : function(state)
6567     {
6568         if(this.active == state){
6569             return;
6570         }
6571         
6572         this.active = state;
6573         
6574         if (state) {
6575             this.el.addClass('active');
6576             return;
6577         }
6578         
6579         this.el.removeClass('active');
6580         
6581         return;
6582     },
6583     
6584     isActive: function () 
6585     {
6586         return this.active;
6587     },
6588     
6589     setBadge : function(str)
6590     {
6591         if(!this.badgeEl){
6592             return;
6593         }
6594         
6595         this.badgeEl.dom.innerHTML = str;
6596     }
6597     
6598    
6599      
6600  
6601 });
6602  
6603
6604  /*
6605  * - LGPL
6606  *
6607  * row
6608  * 
6609  */
6610
6611 /**
6612  * @class Roo.bootstrap.Row
6613  * @extends Roo.bootstrap.Component
6614  * Bootstrap Row class (contains columns...)
6615  * 
6616  * @constructor
6617  * Create a new Row
6618  * @param {Object} config The config object
6619  */
6620
6621 Roo.bootstrap.Row = function(config){
6622     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6623 };
6624
6625 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6626     
6627     getAutoCreate : function(){
6628        return {
6629             cls: 'row clearfix'
6630        };
6631     }
6632     
6633     
6634 });
6635
6636  
6637
6638  /*
6639  * - LGPL
6640  *
6641  * pagination
6642  * 
6643  */
6644
6645 /**
6646  * @class Roo.bootstrap.Pagination
6647  * @extends Roo.bootstrap.Component
6648  * Bootstrap Pagination class
6649  * @cfg {String} size xs | sm | md | lg
6650  * @cfg {Boolean} inverse false | true
6651  * 
6652  * @constructor
6653  * Create a new Pagination
6654  * @param {Object} config The config object
6655  */
6656
6657 Roo.bootstrap.Pagination = function(config){
6658     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6659 };
6660
6661 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6662     
6663     cls: false,
6664     size: false,
6665     inverse: false,
6666     
6667     getAutoCreate : function(){
6668         var cfg = {
6669             tag: 'ul',
6670                 cls: 'pagination'
6671         };
6672         if (this.inverse) {
6673             cfg.cls += ' inverse';
6674         }
6675         if (this.html) {
6676             cfg.html=this.html;
6677         }
6678         if (this.cls) {
6679             cfg.cls += " " + this.cls;
6680         }
6681         return cfg;
6682     }
6683    
6684 });
6685
6686  
6687
6688  /*
6689  * - LGPL
6690  *
6691  * Pagination item
6692  * 
6693  */
6694
6695
6696 /**
6697  * @class Roo.bootstrap.PaginationItem
6698  * @extends Roo.bootstrap.Component
6699  * Bootstrap PaginationItem class
6700  * @cfg {String} html text
6701  * @cfg {String} href the link
6702  * @cfg {Boolean} preventDefault (true | false) default true
6703  * @cfg {Boolean} active (true | false) default false
6704  * @cfg {Boolean} disabled default false
6705  * 
6706  * 
6707  * @constructor
6708  * Create a new PaginationItem
6709  * @param {Object} config The config object
6710  */
6711
6712
6713 Roo.bootstrap.PaginationItem = function(config){
6714     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6715     this.addEvents({
6716         // raw events
6717         /**
6718          * @event click
6719          * The raw click event for the entire grid.
6720          * @param {Roo.EventObject} e
6721          */
6722         "click" : true
6723     });
6724 };
6725
6726 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6727     
6728     href : false,
6729     html : false,
6730     preventDefault: true,
6731     active : false,
6732     cls : false,
6733     disabled: false,
6734     
6735     getAutoCreate : function(){
6736         var cfg= {
6737             tag: 'li',
6738             cn: [
6739                 {
6740                     tag : 'a',
6741                     href : this.href ? this.href : '#',
6742                     html : this.html ? this.html : ''
6743                 }
6744             ]
6745         };
6746         
6747         if(this.cls){
6748             cfg.cls = this.cls;
6749         }
6750         
6751         if(this.disabled){
6752             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6753         }
6754         
6755         if(this.active){
6756             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6757         }
6758         
6759         return cfg;
6760     },
6761     
6762     initEvents: function() {
6763         
6764         this.el.on('click', this.onClick, this);
6765         
6766     },
6767     onClick : function(e)
6768     {
6769         Roo.log('PaginationItem on click ');
6770         if(this.preventDefault){
6771             e.preventDefault();
6772         }
6773         
6774         if(this.disabled){
6775             return;
6776         }
6777         
6778         this.fireEvent('click', this, e);
6779     }
6780    
6781 });
6782
6783  
6784
6785  /*
6786  * - LGPL
6787  *
6788  * slider
6789  * 
6790  */
6791
6792
6793 /**
6794  * @class Roo.bootstrap.Slider
6795  * @extends Roo.bootstrap.Component
6796  * Bootstrap Slider class
6797  *    
6798  * @constructor
6799  * Create a new Slider
6800  * @param {Object} config The config object
6801  */
6802
6803 Roo.bootstrap.Slider = function(config){
6804     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6805 };
6806
6807 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6808     
6809     getAutoCreate : function(){
6810         
6811         var cfg = {
6812             tag: 'div',
6813             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6814             cn: [
6815                 {
6816                     tag: 'a',
6817                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6818                 }
6819             ]
6820         };
6821         
6822         return cfg;
6823     }
6824    
6825 });
6826
6827  /*
6828  * Based on:
6829  * Ext JS Library 1.1.1
6830  * Copyright(c) 2006-2007, Ext JS, LLC.
6831  *
6832  * Originally Released Under LGPL - original licence link has changed is not relivant.
6833  *
6834  * Fork - LGPL
6835  * <script type="text/javascript">
6836  */
6837  
6838
6839 /**
6840  * @class Roo.grid.ColumnModel
6841  * @extends Roo.util.Observable
6842  * This is the default implementation of a ColumnModel used by the Grid. It defines
6843  * the columns in the grid.
6844  * <br>Usage:<br>
6845  <pre><code>
6846  var colModel = new Roo.grid.ColumnModel([
6847         {header: "Ticker", width: 60, sortable: true, locked: true},
6848         {header: "Company Name", width: 150, sortable: true},
6849         {header: "Market Cap.", width: 100, sortable: true},
6850         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6851         {header: "Employees", width: 100, sortable: true, resizable: false}
6852  ]);
6853  </code></pre>
6854  * <p>
6855  
6856  * The config options listed for this class are options which may appear in each
6857  * individual column definition.
6858  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6859  * @constructor
6860  * @param {Object} config An Array of column config objects. See this class's
6861  * config objects for details.
6862 */
6863 Roo.grid.ColumnModel = function(config){
6864         /**
6865      * The config passed into the constructor
6866      */
6867     this.config = config;
6868     this.lookup = {};
6869
6870     // if no id, create one
6871     // if the column does not have a dataIndex mapping,
6872     // map it to the order it is in the config
6873     for(var i = 0, len = config.length; i < len; i++){
6874         var c = config[i];
6875         if(typeof c.dataIndex == "undefined"){
6876             c.dataIndex = i;
6877         }
6878         if(typeof c.renderer == "string"){
6879             c.renderer = Roo.util.Format[c.renderer];
6880         }
6881         if(typeof c.id == "undefined"){
6882             c.id = Roo.id();
6883         }
6884         if(c.editor && c.editor.xtype){
6885             c.editor  = Roo.factory(c.editor, Roo.grid);
6886         }
6887         if(c.editor && c.editor.isFormField){
6888             c.editor = new Roo.grid.GridEditor(c.editor);
6889         }
6890         this.lookup[c.id] = c;
6891     }
6892
6893     /**
6894      * The width of columns which have no width specified (defaults to 100)
6895      * @type Number
6896      */
6897     this.defaultWidth = 100;
6898
6899     /**
6900      * Default sortable of columns which have no sortable specified (defaults to false)
6901      * @type Boolean
6902      */
6903     this.defaultSortable = false;
6904
6905     this.addEvents({
6906         /**
6907              * @event widthchange
6908              * Fires when the width of a column changes.
6909              * @param {ColumnModel} this
6910              * @param {Number} columnIndex The column index
6911              * @param {Number} newWidth The new width
6912              */
6913             "widthchange": true,
6914         /**
6915              * @event headerchange
6916              * Fires when the text of a header changes.
6917              * @param {ColumnModel} this
6918              * @param {Number} columnIndex The column index
6919              * @param {Number} newText The new header text
6920              */
6921             "headerchange": true,
6922         /**
6923              * @event hiddenchange
6924              * Fires when a column is hidden or "unhidden".
6925              * @param {ColumnModel} this
6926              * @param {Number} columnIndex The column index
6927              * @param {Boolean} hidden true if hidden, false otherwise
6928              */
6929             "hiddenchange": true,
6930             /**
6931          * @event columnmoved
6932          * Fires when a column is moved.
6933          * @param {ColumnModel} this
6934          * @param {Number} oldIndex
6935          * @param {Number} newIndex
6936          */
6937         "columnmoved" : true,
6938         /**
6939          * @event columlockchange
6940          * Fires when a column's locked state is changed
6941          * @param {ColumnModel} this
6942          * @param {Number} colIndex
6943          * @param {Boolean} locked true if locked
6944          */
6945         "columnlockchange" : true
6946     });
6947     Roo.grid.ColumnModel.superclass.constructor.call(this);
6948 };
6949 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6950     /**
6951      * @cfg {String} header The header text to display in the Grid view.
6952      */
6953     /**
6954      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6955      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6956      * specified, the column's index is used as an index into the Record's data Array.
6957      */
6958     /**
6959      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6960      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6961      */
6962     /**
6963      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6964      * Defaults to the value of the {@link #defaultSortable} property.
6965      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6966      */
6967     /**
6968      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6969      */
6970     /**
6971      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6972      */
6973     /**
6974      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6975      */
6976     /**
6977      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6978      */
6979     /**
6980      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6981      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6982      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6983      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6984      */
6985        /**
6986      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6987      */
6988     /**
6989      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6990      */
6991     /**
6992      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6993      */
6994     /**
6995      * @cfg {String} cursor (Optional)
6996      */
6997     /**
6998      * @cfg {String} tooltip (Optional)
6999      */
7000     /**
7001      * @cfg {Number} xs (Optional)
7002      */
7003     /**
7004      * @cfg {Number} sm (Optional)
7005      */
7006     /**
7007      * @cfg {Number} md (Optional)
7008      */
7009     /**
7010      * @cfg {Number} lg (Optional)
7011      */
7012     /**
7013      * Returns the id of the column at the specified index.
7014      * @param {Number} index The column index
7015      * @return {String} the id
7016      */
7017     getColumnId : function(index){
7018         return this.config[index].id;
7019     },
7020
7021     /**
7022      * Returns the column for a specified id.
7023      * @param {String} id The column id
7024      * @return {Object} the column
7025      */
7026     getColumnById : function(id){
7027         return this.lookup[id];
7028     },
7029
7030     
7031     /**
7032      * Returns the column for a specified dataIndex.
7033      * @param {String} dataIndex The column dataIndex
7034      * @return {Object|Boolean} the column or false if not found
7035      */
7036     getColumnByDataIndex: function(dataIndex){
7037         var index = this.findColumnIndex(dataIndex);
7038         return index > -1 ? this.config[index] : false;
7039     },
7040     
7041     /**
7042      * Returns the index for a specified column id.
7043      * @param {String} id The column id
7044      * @return {Number} the index, or -1 if not found
7045      */
7046     getIndexById : function(id){
7047         for(var i = 0, len = this.config.length; i < len; i++){
7048             if(this.config[i].id == id){
7049                 return i;
7050             }
7051         }
7052         return -1;
7053     },
7054     
7055     /**
7056      * Returns the index for a specified column dataIndex.
7057      * @param {String} dataIndex The column dataIndex
7058      * @return {Number} the index, or -1 if not found
7059      */
7060     
7061     findColumnIndex : function(dataIndex){
7062         for(var i = 0, len = this.config.length; i < len; i++){
7063             if(this.config[i].dataIndex == dataIndex){
7064                 return i;
7065             }
7066         }
7067         return -1;
7068     },
7069     
7070     
7071     moveColumn : function(oldIndex, newIndex){
7072         var c = this.config[oldIndex];
7073         this.config.splice(oldIndex, 1);
7074         this.config.splice(newIndex, 0, c);
7075         this.dataMap = null;
7076         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7077     },
7078
7079     isLocked : function(colIndex){
7080         return this.config[colIndex].locked === true;
7081     },
7082
7083     setLocked : function(colIndex, value, suppressEvent){
7084         if(this.isLocked(colIndex) == value){
7085             return;
7086         }
7087         this.config[colIndex].locked = value;
7088         if(!suppressEvent){
7089             this.fireEvent("columnlockchange", this, colIndex, value);
7090         }
7091     },
7092
7093     getTotalLockedWidth : function(){
7094         var totalWidth = 0;
7095         for(var i = 0; i < this.config.length; i++){
7096             if(this.isLocked(i) && !this.isHidden(i)){
7097                 this.totalWidth += this.getColumnWidth(i);
7098             }
7099         }
7100         return totalWidth;
7101     },
7102
7103     getLockedCount : function(){
7104         for(var i = 0, len = this.config.length; i < len; i++){
7105             if(!this.isLocked(i)){
7106                 return i;
7107             }
7108         }
7109         
7110         return this.config.length;
7111     },
7112
7113     /**
7114      * Returns the number of columns.
7115      * @return {Number}
7116      */
7117     getColumnCount : function(visibleOnly){
7118         if(visibleOnly === true){
7119             var c = 0;
7120             for(var i = 0, len = this.config.length; i < len; i++){
7121                 if(!this.isHidden(i)){
7122                     c++;
7123                 }
7124             }
7125             return c;
7126         }
7127         return this.config.length;
7128     },
7129
7130     /**
7131      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7132      * @param {Function} fn
7133      * @param {Object} scope (optional)
7134      * @return {Array} result
7135      */
7136     getColumnsBy : function(fn, scope){
7137         var r = [];
7138         for(var i = 0, len = this.config.length; i < len; i++){
7139             var c = this.config[i];
7140             if(fn.call(scope||this, c, i) === true){
7141                 r[r.length] = c;
7142             }
7143         }
7144         return r;
7145     },
7146
7147     /**
7148      * Returns true if the specified column is sortable.
7149      * @param {Number} col The column index
7150      * @return {Boolean}
7151      */
7152     isSortable : function(col){
7153         if(typeof this.config[col].sortable == "undefined"){
7154             return this.defaultSortable;
7155         }
7156         return this.config[col].sortable;
7157     },
7158
7159     /**
7160      * Returns the rendering (formatting) function defined for the column.
7161      * @param {Number} col The column index.
7162      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7163      */
7164     getRenderer : function(col){
7165         if(!this.config[col].renderer){
7166             return Roo.grid.ColumnModel.defaultRenderer;
7167         }
7168         return this.config[col].renderer;
7169     },
7170
7171     /**
7172      * Sets the rendering (formatting) function for a column.
7173      * @param {Number} col The column index
7174      * @param {Function} fn The function to use to process the cell's raw data
7175      * to return HTML markup for the grid view. The render function is called with
7176      * the following parameters:<ul>
7177      * <li>Data value.</li>
7178      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7179      * <li>css A CSS style string to apply to the table cell.</li>
7180      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7181      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7182      * <li>Row index</li>
7183      * <li>Column index</li>
7184      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7185      */
7186     setRenderer : function(col, fn){
7187         this.config[col].renderer = fn;
7188     },
7189
7190     /**
7191      * Returns the width for the specified column.
7192      * @param {Number} col The column index
7193      * @return {Number}
7194      */
7195     getColumnWidth : function(col){
7196         return this.config[col].width * 1 || this.defaultWidth;
7197     },
7198
7199     /**
7200      * Sets the width for a column.
7201      * @param {Number} col The column index
7202      * @param {Number} width The new width
7203      */
7204     setColumnWidth : function(col, width, suppressEvent){
7205         this.config[col].width = width;
7206         this.totalWidth = null;
7207         if(!suppressEvent){
7208              this.fireEvent("widthchange", this, col, width);
7209         }
7210     },
7211
7212     /**
7213      * Returns the total width of all columns.
7214      * @param {Boolean} includeHidden True to include hidden column widths
7215      * @return {Number}
7216      */
7217     getTotalWidth : function(includeHidden){
7218         if(!this.totalWidth){
7219             this.totalWidth = 0;
7220             for(var i = 0, len = this.config.length; i < len; i++){
7221                 if(includeHidden || !this.isHidden(i)){
7222                     this.totalWidth += this.getColumnWidth(i);
7223                 }
7224             }
7225         }
7226         return this.totalWidth;
7227     },
7228
7229     /**
7230      * Returns the header for the specified column.
7231      * @param {Number} col The column index
7232      * @return {String}
7233      */
7234     getColumnHeader : function(col){
7235         return this.config[col].header;
7236     },
7237
7238     /**
7239      * Sets the header for a column.
7240      * @param {Number} col The column index
7241      * @param {String} header The new header
7242      */
7243     setColumnHeader : function(col, header){
7244         this.config[col].header = header;
7245         this.fireEvent("headerchange", this, col, header);
7246     },
7247
7248     /**
7249      * Returns the tooltip for the specified column.
7250      * @param {Number} col The column index
7251      * @return {String}
7252      */
7253     getColumnTooltip : function(col){
7254             return this.config[col].tooltip;
7255     },
7256     /**
7257      * Sets the tooltip for a column.
7258      * @param {Number} col The column index
7259      * @param {String} tooltip The new tooltip
7260      */
7261     setColumnTooltip : function(col, tooltip){
7262             this.config[col].tooltip = tooltip;
7263     },
7264
7265     /**
7266      * Returns the dataIndex for the specified column.
7267      * @param {Number} col The column index
7268      * @return {Number}
7269      */
7270     getDataIndex : function(col){
7271         return this.config[col].dataIndex;
7272     },
7273
7274     /**
7275      * Sets the dataIndex for a column.
7276      * @param {Number} col The column index
7277      * @param {Number} dataIndex The new dataIndex
7278      */
7279     setDataIndex : function(col, dataIndex){
7280         this.config[col].dataIndex = dataIndex;
7281     },
7282
7283     
7284     
7285     /**
7286      * Returns true if the cell is editable.
7287      * @param {Number} colIndex The column index
7288      * @param {Number} rowIndex The row index - this is nto actually used..?
7289      * @return {Boolean}
7290      */
7291     isCellEditable : function(colIndex, rowIndex){
7292         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7293     },
7294
7295     /**
7296      * Returns the editor defined for the cell/column.
7297      * return false or null to disable editing.
7298      * @param {Number} colIndex The column index
7299      * @param {Number} rowIndex The row index
7300      * @return {Object}
7301      */
7302     getCellEditor : function(colIndex, rowIndex){
7303         return this.config[colIndex].editor;
7304     },
7305
7306     /**
7307      * Sets if a column is editable.
7308      * @param {Number} col The column index
7309      * @param {Boolean} editable True if the column is editable
7310      */
7311     setEditable : function(col, editable){
7312         this.config[col].editable = editable;
7313     },
7314
7315
7316     /**
7317      * Returns true if the column is hidden.
7318      * @param {Number} colIndex The column index
7319      * @return {Boolean}
7320      */
7321     isHidden : function(colIndex){
7322         return this.config[colIndex].hidden;
7323     },
7324
7325
7326     /**
7327      * Returns true if the column width cannot be changed
7328      */
7329     isFixed : function(colIndex){
7330         return this.config[colIndex].fixed;
7331     },
7332
7333     /**
7334      * Returns true if the column can be resized
7335      * @return {Boolean}
7336      */
7337     isResizable : function(colIndex){
7338         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7339     },
7340     /**
7341      * Sets if a column is hidden.
7342      * @param {Number} colIndex The column index
7343      * @param {Boolean} hidden True if the column is hidden
7344      */
7345     setHidden : function(colIndex, hidden){
7346         this.config[colIndex].hidden = hidden;
7347         this.totalWidth = null;
7348         this.fireEvent("hiddenchange", this, colIndex, hidden);
7349     },
7350
7351     /**
7352      * Sets the editor for a column.
7353      * @param {Number} col The column index
7354      * @param {Object} editor The editor object
7355      */
7356     setEditor : function(col, editor){
7357         this.config[col].editor = editor;
7358     }
7359 });
7360
7361 Roo.grid.ColumnModel.defaultRenderer = function(value)
7362 {
7363     if(typeof value == "object") {
7364         return value;
7365     }
7366         if(typeof value == "string" && value.length < 1){
7367             return "&#160;";
7368         }
7369     
7370         return String.format("{0}", value);
7371 };
7372
7373 // Alias for backwards compatibility
7374 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7375 /*
7376  * Based on:
7377  * Ext JS Library 1.1.1
7378  * Copyright(c) 2006-2007, Ext JS, LLC.
7379  *
7380  * Originally Released Under LGPL - original licence link has changed is not relivant.
7381  *
7382  * Fork - LGPL
7383  * <script type="text/javascript">
7384  */
7385  
7386 /**
7387  * @class Roo.LoadMask
7388  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7389  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7390  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7391  * element's UpdateManager load indicator and will be destroyed after the initial load.
7392  * @constructor
7393  * Create a new LoadMask
7394  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7395  * @param {Object} config The config object
7396  */
7397 Roo.LoadMask = function(el, config){
7398     this.el = Roo.get(el);
7399     Roo.apply(this, config);
7400     if(this.store){
7401         this.store.on('beforeload', this.onBeforeLoad, this);
7402         this.store.on('load', this.onLoad, this);
7403         this.store.on('loadexception', this.onLoadException, this);
7404         this.removeMask = false;
7405     }else{
7406         var um = this.el.getUpdateManager();
7407         um.showLoadIndicator = false; // disable the default indicator
7408         um.on('beforeupdate', this.onBeforeLoad, this);
7409         um.on('update', this.onLoad, this);
7410         um.on('failure', this.onLoad, this);
7411         this.removeMask = true;
7412     }
7413 };
7414
7415 Roo.LoadMask.prototype = {
7416     /**
7417      * @cfg {Boolean} removeMask
7418      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7419      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7420      */
7421     /**
7422      * @cfg {String} msg
7423      * The text to display in a centered loading message box (defaults to 'Loading...')
7424      */
7425     msg : 'Loading...',
7426     /**
7427      * @cfg {String} msgCls
7428      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7429      */
7430     msgCls : 'x-mask-loading',
7431
7432     /**
7433      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7434      * @type Boolean
7435      */
7436     disabled: false,
7437
7438     /**
7439      * Disables the mask to prevent it from being displayed
7440      */
7441     disable : function(){
7442        this.disabled = true;
7443     },
7444
7445     /**
7446      * Enables the mask so that it can be displayed
7447      */
7448     enable : function(){
7449         this.disabled = false;
7450     },
7451     
7452     onLoadException : function()
7453     {
7454         Roo.log(arguments);
7455         
7456         if (typeof(arguments[3]) != 'undefined') {
7457             Roo.MessageBox.alert("Error loading",arguments[3]);
7458         } 
7459         /*
7460         try {
7461             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7462                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7463             }   
7464         } catch(e) {
7465             
7466         }
7467         */
7468     
7469         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7470     },
7471     // private
7472     onLoad : function()
7473     {
7474         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7475     },
7476
7477     // private
7478     onBeforeLoad : function(){
7479         if(!this.disabled){
7480             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7481         }
7482     },
7483
7484     // private
7485     destroy : function(){
7486         if(this.store){
7487             this.store.un('beforeload', this.onBeforeLoad, this);
7488             this.store.un('load', this.onLoad, this);
7489             this.store.un('loadexception', this.onLoadException, this);
7490         }else{
7491             var um = this.el.getUpdateManager();
7492             um.un('beforeupdate', this.onBeforeLoad, this);
7493             um.un('update', this.onLoad, this);
7494             um.un('failure', this.onLoad, this);
7495         }
7496     }
7497 };/*
7498  * - LGPL
7499  *
7500  * table
7501  * 
7502  */
7503
7504 /**
7505  * @class Roo.bootstrap.Table
7506  * @extends Roo.bootstrap.Component
7507  * Bootstrap Table class
7508  * @cfg {String} cls table class
7509  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7510  * @cfg {String} bgcolor Specifies the background color for a table
7511  * @cfg {Number} border Specifies whether the table cells should have borders or not
7512  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7513  * @cfg {Number} cellspacing Specifies the space between cells
7514  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7515  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7516  * @cfg {String} sortable Specifies that the table should be sortable
7517  * @cfg {String} summary Specifies a summary of the content of a table
7518  * @cfg {Number} width Specifies the width of a table
7519  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7520  * 
7521  * @cfg {boolean} striped Should the rows be alternative striped
7522  * @cfg {boolean} bordered Add borders to the table
7523  * @cfg {boolean} hover Add hover highlighting
7524  * @cfg {boolean} condensed Format condensed
7525  * @cfg {boolean} responsive Format condensed
7526  * @cfg {Boolean} loadMask (true|false) default false
7527  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7528  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7529  * @cfg {Boolean} rowSelection (true|false) default false
7530  * @cfg {Boolean} cellSelection (true|false) default false
7531  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7532  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7533  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7534  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7535  
7536  * 
7537  * @constructor
7538  * Create a new Table
7539  * @param {Object} config The config object
7540  */
7541
7542 Roo.bootstrap.Table = function(config){
7543     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7544     
7545   
7546     
7547     // BC...
7548     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7549     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7550     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7551     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7552     
7553     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7554     if (this.sm) {
7555         this.sm.grid = this;
7556         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7557         this.sm = this.selModel;
7558         this.sm.xmodule = this.xmodule || false;
7559     }
7560     
7561     if (this.cm && typeof(this.cm.config) == 'undefined') {
7562         this.colModel = new Roo.grid.ColumnModel(this.cm);
7563         this.cm = this.colModel;
7564         this.cm.xmodule = this.xmodule || false;
7565     }
7566     if (this.store) {
7567         this.store= Roo.factory(this.store, Roo.data);
7568         this.ds = this.store;
7569         this.ds.xmodule = this.xmodule || false;
7570          
7571     }
7572     if (this.footer && this.store) {
7573         this.footer.dataSource = this.ds;
7574         this.footer = Roo.factory(this.footer);
7575     }
7576     
7577     /** @private */
7578     this.addEvents({
7579         /**
7580          * @event cellclick
7581          * Fires when a cell is clicked
7582          * @param {Roo.bootstrap.Table} this
7583          * @param {Roo.Element} el
7584          * @param {Number} rowIndex
7585          * @param {Number} columnIndex
7586          * @param {Roo.EventObject} e
7587          */
7588         "cellclick" : true,
7589         /**
7590          * @event celldblclick
7591          * Fires when a cell is double clicked
7592          * @param {Roo.bootstrap.Table} this
7593          * @param {Roo.Element} el
7594          * @param {Number} rowIndex
7595          * @param {Number} columnIndex
7596          * @param {Roo.EventObject} e
7597          */
7598         "celldblclick" : true,
7599         /**
7600          * @event rowclick
7601          * Fires when a row is clicked
7602          * @param {Roo.bootstrap.Table} this
7603          * @param {Roo.Element} el
7604          * @param {Number} rowIndex
7605          * @param {Roo.EventObject} e
7606          */
7607         "rowclick" : true,
7608         /**
7609          * @event rowdblclick
7610          * Fires when a row is double clicked
7611          * @param {Roo.bootstrap.Table} this
7612          * @param {Roo.Element} el
7613          * @param {Number} rowIndex
7614          * @param {Roo.EventObject} e
7615          */
7616         "rowdblclick" : true,
7617         /**
7618          * @event mouseover
7619          * Fires when a mouseover occur
7620          * @param {Roo.bootstrap.Table} this
7621          * @param {Roo.Element} el
7622          * @param {Number} rowIndex
7623          * @param {Number} columnIndex
7624          * @param {Roo.EventObject} e
7625          */
7626         "mouseover" : true,
7627         /**
7628          * @event mouseout
7629          * Fires when a mouseout occur
7630          * @param {Roo.bootstrap.Table} this
7631          * @param {Roo.Element} el
7632          * @param {Number} rowIndex
7633          * @param {Number} columnIndex
7634          * @param {Roo.EventObject} e
7635          */
7636         "mouseout" : true,
7637         /**
7638          * @event rowclass
7639          * Fires when a row is rendered, so you can change add a style to it.
7640          * @param {Roo.bootstrap.Table} this
7641          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7642          */
7643         'rowclass' : true,
7644           /**
7645          * @event rowsrendered
7646          * Fires when all the  rows have been rendered
7647          * @param {Roo.bootstrap.Table} this
7648          */
7649         'rowsrendered' : true,
7650         /**
7651          * @event contextmenu
7652          * The raw contextmenu event for the entire grid.
7653          * @param {Roo.EventObject} e
7654          */
7655         "contextmenu" : true,
7656         /**
7657          * @event rowcontextmenu
7658          * Fires when a row is right clicked
7659          * @param {Roo.bootstrap.Table} this
7660          * @param {Number} rowIndex
7661          * @param {Roo.EventObject} e
7662          */
7663         "rowcontextmenu" : true,
7664         /**
7665          * @event cellcontextmenu
7666          * Fires when a cell is right clicked
7667          * @param {Roo.bootstrap.Table} this
7668          * @param {Number} rowIndex
7669          * @param {Number} cellIndex
7670          * @param {Roo.EventObject} e
7671          */
7672          "cellcontextmenu" : true,
7673          /**
7674          * @event headercontextmenu
7675          * Fires when a header is right clicked
7676          * @param {Roo.bootstrap.Table} this
7677          * @param {Number} columnIndex
7678          * @param {Roo.EventObject} e
7679          */
7680         "headercontextmenu" : true
7681     });
7682 };
7683
7684 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7685     
7686     cls: false,
7687     align: false,
7688     bgcolor: false,
7689     border: false,
7690     cellpadding: false,
7691     cellspacing: false,
7692     frame: false,
7693     rules: false,
7694     sortable: false,
7695     summary: false,
7696     width: false,
7697     striped : false,
7698     scrollBody : false,
7699     bordered: false,
7700     hover:  false,
7701     condensed : false,
7702     responsive : false,
7703     sm : false,
7704     cm : false,
7705     store : false,
7706     loadMask : false,
7707     footerShow : true,
7708     headerShow : true,
7709   
7710     rowSelection : false,
7711     cellSelection : false,
7712     layout : false,
7713     
7714     // Roo.Element - the tbody
7715     mainBody: false,
7716     // Roo.Element - thead element
7717     mainHead: false,
7718     
7719     container: false, // used by gridpanel...
7720     
7721     lazyLoad : false,
7722     
7723     CSS : Roo.util.CSS,
7724     
7725     auto_hide_footer : false,
7726     
7727     getAutoCreate : function()
7728     {
7729         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7730         
7731         cfg = {
7732             tag: 'table',
7733             cls : 'table',
7734             cn : []
7735         };
7736         if (this.scrollBody) {
7737             cfg.cls += ' table-body-fixed';
7738         }    
7739         if (this.striped) {
7740             cfg.cls += ' table-striped';
7741         }
7742         
7743         if (this.hover) {
7744             cfg.cls += ' table-hover';
7745         }
7746         if (this.bordered) {
7747             cfg.cls += ' table-bordered';
7748         }
7749         if (this.condensed) {
7750             cfg.cls += ' table-condensed';
7751         }
7752         if (this.responsive) {
7753             cfg.cls += ' table-responsive';
7754         }
7755         
7756         if (this.cls) {
7757             cfg.cls+=  ' ' +this.cls;
7758         }
7759         
7760         // this lot should be simplifed...
7761         var _t = this;
7762         var cp = [
7763             'align',
7764             'bgcolor',
7765             'border',
7766             'cellpadding',
7767             'cellspacing',
7768             'frame',
7769             'rules',
7770             'sortable',
7771             'summary',
7772             'width'
7773         ].forEach(function(k) {
7774             if (_t[k]) {
7775                 cfg[k] = _t[k];
7776             }
7777         });
7778         
7779         
7780         if (this.layout) {
7781             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7782         }
7783         
7784         if(this.store || this.cm){
7785             if(this.headerShow){
7786                 cfg.cn.push(this.renderHeader());
7787             }
7788             
7789             cfg.cn.push(this.renderBody());
7790             
7791             if(this.footerShow){
7792                 cfg.cn.push(this.renderFooter());
7793             }
7794             // where does this come from?
7795             //cfg.cls+=  ' TableGrid';
7796         }
7797         
7798         return { cn : [ cfg ] };
7799     },
7800     
7801     initEvents : function()
7802     {   
7803         if(!this.store || !this.cm){
7804             return;
7805         }
7806         if (this.selModel) {
7807             this.selModel.initEvents();
7808         }
7809         
7810         
7811         //Roo.log('initEvents with ds!!!!');
7812         
7813         this.mainBody = this.el.select('tbody', true).first();
7814         this.mainHead = this.el.select('thead', true).first();
7815         this.mainFoot = this.el.select('tfoot', true).first();
7816         
7817         
7818         
7819         var _this = this;
7820         
7821         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7822             e.on('click', _this.sort, _this);
7823         });
7824         
7825         this.mainBody.on("click", this.onClick, this);
7826         this.mainBody.on("dblclick", this.onDblClick, this);
7827         
7828         // why is this done????? = it breaks dialogs??
7829         //this.parent().el.setStyle('position', 'relative');
7830         
7831         
7832         if (this.footer) {
7833             this.footer.parentId = this.id;
7834             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7835             
7836             if(this.lazyLoad){
7837                 this.el.select('tfoot tr td').first().addClass('hide');
7838             }
7839         } 
7840         
7841         if(this.loadMask) {
7842             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7843         }
7844         
7845         this.store.on('load', this.onLoad, this);
7846         this.store.on('beforeload', this.onBeforeLoad, this);
7847         this.store.on('update', this.onUpdate, this);
7848         this.store.on('add', this.onAdd, this);
7849         this.store.on("clear", this.clear, this);
7850         
7851         this.el.on("contextmenu", this.onContextMenu, this);
7852         
7853         this.mainBody.on('scroll', this.onBodyScroll, this);
7854         
7855         this.cm.on("headerchange", this.onHeaderChange, this);
7856         
7857         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7858         
7859     },
7860     
7861     onContextMenu : function(e, t)
7862     {
7863         this.processEvent("contextmenu", e);
7864     },
7865     
7866     processEvent : function(name, e)
7867     {
7868         if (name != 'touchstart' ) {
7869             this.fireEvent(name, e);    
7870         }
7871         
7872         var t = e.getTarget();
7873         
7874         var cell = Roo.get(t);
7875         
7876         if(!cell){
7877             return;
7878         }
7879         
7880         if(cell.findParent('tfoot', false, true)){
7881             return;
7882         }
7883         
7884         if(cell.findParent('thead', false, true)){
7885             
7886             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7887                 cell = Roo.get(t).findParent('th', false, true);
7888                 if (!cell) {
7889                     Roo.log("failed to find th in thead?");
7890                     Roo.log(e.getTarget());
7891                     return;
7892                 }
7893             }
7894             
7895             var cellIndex = cell.dom.cellIndex;
7896             
7897             var ename = name == 'touchstart' ? 'click' : name;
7898             this.fireEvent("header" + ename, this, cellIndex, e);
7899             
7900             return;
7901         }
7902         
7903         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7904             cell = Roo.get(t).findParent('td', false, true);
7905             if (!cell) {
7906                 Roo.log("failed to find th in tbody?");
7907                 Roo.log(e.getTarget());
7908                 return;
7909             }
7910         }
7911         
7912         var row = cell.findParent('tr', false, true);
7913         var cellIndex = cell.dom.cellIndex;
7914         var rowIndex = row.dom.rowIndex - 1;
7915         
7916         if(row !== false){
7917             
7918             this.fireEvent("row" + name, this, rowIndex, e);
7919             
7920             if(cell !== false){
7921             
7922                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7923             }
7924         }
7925         
7926     },
7927     
7928     onMouseover : function(e, el)
7929     {
7930         var cell = Roo.get(el);
7931         
7932         if(!cell){
7933             return;
7934         }
7935         
7936         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7937             cell = cell.findParent('td', false, true);
7938         }
7939         
7940         var row = cell.findParent('tr', false, true);
7941         var cellIndex = cell.dom.cellIndex;
7942         var rowIndex = row.dom.rowIndex - 1; // start from 0
7943         
7944         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7945         
7946     },
7947     
7948     onMouseout : function(e, el)
7949     {
7950         var cell = Roo.get(el);
7951         
7952         if(!cell){
7953             return;
7954         }
7955         
7956         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7957             cell = cell.findParent('td', false, true);
7958         }
7959         
7960         var row = cell.findParent('tr', false, true);
7961         var cellIndex = cell.dom.cellIndex;
7962         var rowIndex = row.dom.rowIndex - 1; // start from 0
7963         
7964         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7965         
7966     },
7967     
7968     onClick : function(e, el)
7969     {
7970         var cell = Roo.get(el);
7971         
7972         if(!cell || (!this.cellSelection && !this.rowSelection)){
7973             return;
7974         }
7975         
7976         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7977             cell = cell.findParent('td', false, true);
7978         }
7979         
7980         if(!cell || typeof(cell) == 'undefined'){
7981             return;
7982         }
7983         
7984         var row = cell.findParent('tr', false, true);
7985         
7986         if(!row || typeof(row) == 'undefined'){
7987             return;
7988         }
7989         
7990         var cellIndex = cell.dom.cellIndex;
7991         var rowIndex = this.getRowIndex(row);
7992         
7993         // why??? - should these not be based on SelectionModel?
7994         if(this.cellSelection){
7995             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7996         }
7997         
7998         if(this.rowSelection){
7999             this.fireEvent('rowclick', this, row, rowIndex, e);
8000         }
8001         
8002         
8003     },
8004         
8005     onDblClick : function(e,el)
8006     {
8007         var cell = Roo.get(el);
8008         
8009         if(!cell || (!this.cellSelection && !this.rowSelection)){
8010             return;
8011         }
8012         
8013         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8014             cell = cell.findParent('td', false, true);
8015         }
8016         
8017         if(!cell || typeof(cell) == 'undefined'){
8018             return;
8019         }
8020         
8021         var row = cell.findParent('tr', false, true);
8022         
8023         if(!row || typeof(row) == 'undefined'){
8024             return;
8025         }
8026         
8027         var cellIndex = cell.dom.cellIndex;
8028         var rowIndex = this.getRowIndex(row);
8029         
8030         if(this.cellSelection){
8031             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8032         }
8033         
8034         if(this.rowSelection){
8035             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8036         }
8037     },
8038     
8039     sort : function(e,el)
8040     {
8041         var col = Roo.get(el);
8042         
8043         if(!col.hasClass('sortable')){
8044             return;
8045         }
8046         
8047         var sort = col.attr('sort');
8048         var dir = 'ASC';
8049         
8050         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8051             dir = 'DESC';
8052         }
8053         
8054         this.store.sortInfo = {field : sort, direction : dir};
8055         
8056         if (this.footer) {
8057             Roo.log("calling footer first");
8058             this.footer.onClick('first');
8059         } else {
8060         
8061             this.store.load({ params : { start : 0 } });
8062         }
8063     },
8064     
8065     renderHeader : function()
8066     {
8067         var header = {
8068             tag: 'thead',
8069             cn : []
8070         };
8071         
8072         var cm = this.cm;
8073         this.totalWidth = 0;
8074         
8075         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8076             
8077             var config = cm.config[i];
8078             
8079             var c = {
8080                 tag: 'th',
8081                 cls : 'x-hcol-' + i,
8082                 style : '',
8083                 html: cm.getColumnHeader(i)
8084             };
8085             
8086             var hh = '';
8087             
8088             if(typeof(config.sortable) != 'undefined' && config.sortable){
8089                 c.cls = 'sortable';
8090                 c.html = '<i class="glyphicon"></i>' + c.html;
8091             }
8092             
8093             // could use BS4 hidden-..-down 
8094             
8095             if(typeof(config.lgHeader) != 'undefined'){
8096                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8097             }
8098             
8099             if(typeof(config.mdHeader) != 'undefined'){
8100                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8101             }
8102             
8103             if(typeof(config.smHeader) != 'undefined'){
8104                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8105             }
8106             
8107             if(typeof(config.xsHeader) != 'undefined'){
8108                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8109             }
8110             
8111             if(hh.length){
8112                 c.html = hh;
8113             }
8114             
8115             if(typeof(config.tooltip) != 'undefined'){
8116                 c.tooltip = config.tooltip;
8117             }
8118             
8119             if(typeof(config.colspan) != 'undefined'){
8120                 c.colspan = config.colspan;
8121             }
8122             
8123             if(typeof(config.hidden) != 'undefined' && config.hidden){
8124                 c.style += ' display:none;';
8125             }
8126             
8127             if(typeof(config.dataIndex) != 'undefined'){
8128                 c.sort = config.dataIndex;
8129             }
8130             
8131            
8132             
8133             if(typeof(config.align) != 'undefined' && config.align.length){
8134                 c.style += ' text-align:' + config.align + ';';
8135             }
8136             
8137             if(typeof(config.width) != 'undefined'){
8138                 c.style += ' width:' + config.width + 'px;';
8139                 this.totalWidth += config.width;
8140             } else {
8141                 this.totalWidth += 100; // assume minimum of 100 per column?
8142             }
8143             
8144             if(typeof(config.cls) != 'undefined'){
8145                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8146             }
8147             
8148             ['xs','sm','md','lg'].map(function(size){
8149                 
8150                 if(typeof(config[size]) == 'undefined'){
8151                     return;
8152                 }
8153                  
8154                 if (!config[size]) { // 0 = hidden
8155                     // BS 4 '0' is treated as hide that column and below.
8156                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8157                     return;
8158                 }
8159                 
8160                 c.cls += ' col-' + size + '-' + config[size] + (
8161                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8162                 );
8163                 
8164                 
8165             });
8166             
8167             header.cn.push(c)
8168         }
8169         
8170         return header;
8171     },
8172     
8173     renderBody : function()
8174     {
8175         var body = {
8176             tag: 'tbody',
8177             cn : [
8178                 {
8179                     tag: 'tr',
8180                     cn : [
8181                         {
8182                             tag : 'td',
8183                             colspan :  this.cm.getColumnCount()
8184                         }
8185                     ]
8186                 }
8187             ]
8188         };
8189         
8190         return body;
8191     },
8192     
8193     renderFooter : function()
8194     {
8195         var footer = {
8196             tag: 'tfoot',
8197             cn : [
8198                 {
8199                     tag: 'tr',
8200                     cn : [
8201                         {
8202                             tag : 'td',
8203                             colspan :  this.cm.getColumnCount()
8204                         }
8205                     ]
8206                 }
8207             ]
8208         };
8209         
8210         return footer;
8211     },
8212     
8213     
8214     
8215     onLoad : function()
8216     {
8217 //        Roo.log('ds onload');
8218         this.clear();
8219         
8220         var _this = this;
8221         var cm = this.cm;
8222         var ds = this.store;
8223         
8224         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8225             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8226             if (_this.store.sortInfo) {
8227                     
8228                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8229                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8230                 }
8231                 
8232                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8233                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8234                 }
8235             }
8236         });
8237         
8238         var tbody =  this.mainBody;
8239               
8240         if(ds.getCount() > 0){
8241             ds.data.each(function(d,rowIndex){
8242                 var row =  this.renderRow(cm, ds, rowIndex);
8243                 
8244                 tbody.createChild(row);
8245                 
8246                 var _this = this;
8247                 
8248                 if(row.cellObjects.length){
8249                     Roo.each(row.cellObjects, function(r){
8250                         _this.renderCellObject(r);
8251                     })
8252                 }
8253                 
8254             }, this);
8255         }
8256         
8257         var tfoot = this.el.select('tfoot', true).first();
8258         
8259         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8260             
8261             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8262             
8263             var total = this.ds.getTotalCount();
8264             
8265             if(this.footer.pageSize < total){
8266                 this.mainFoot.show();
8267             }
8268         }
8269         
8270         Roo.each(this.el.select('tbody td', true).elements, function(e){
8271             e.on('mouseover', _this.onMouseover, _this);
8272         });
8273         
8274         Roo.each(this.el.select('tbody td', true).elements, function(e){
8275             e.on('mouseout', _this.onMouseout, _this);
8276         });
8277         this.fireEvent('rowsrendered', this);
8278         
8279         this.autoSize();
8280     },
8281     
8282     
8283     onUpdate : function(ds,record)
8284     {
8285         this.refreshRow(record);
8286         this.autoSize();
8287     },
8288     
8289     onRemove : function(ds, record, index, isUpdate){
8290         if(isUpdate !== true){
8291             this.fireEvent("beforerowremoved", this, index, record);
8292         }
8293         var bt = this.mainBody.dom;
8294         
8295         var rows = this.el.select('tbody > tr', true).elements;
8296         
8297         if(typeof(rows[index]) != 'undefined'){
8298             bt.removeChild(rows[index].dom);
8299         }
8300         
8301 //        if(bt.rows[index]){
8302 //            bt.removeChild(bt.rows[index]);
8303 //        }
8304         
8305         if(isUpdate !== true){
8306             //this.stripeRows(index);
8307             //this.syncRowHeights(index, index);
8308             //this.layout();
8309             this.fireEvent("rowremoved", this, index, record);
8310         }
8311     },
8312     
8313     onAdd : function(ds, records, rowIndex)
8314     {
8315         //Roo.log('on Add called');
8316         // - note this does not handle multiple adding very well..
8317         var bt = this.mainBody.dom;
8318         for (var i =0 ; i < records.length;i++) {
8319             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8320             //Roo.log(records[i]);
8321             //Roo.log(this.store.getAt(rowIndex+i));
8322             this.insertRow(this.store, rowIndex + i, false);
8323             return;
8324         }
8325         
8326     },
8327     
8328     
8329     refreshRow : function(record){
8330         var ds = this.store, index;
8331         if(typeof record == 'number'){
8332             index = record;
8333             record = ds.getAt(index);
8334         }else{
8335             index = ds.indexOf(record);
8336             if (index < 0) {
8337                 return; // should not happen - but seems to 
8338             }
8339         }
8340         this.insertRow(ds, index, true);
8341         this.autoSize();
8342         this.onRemove(ds, record, index+1, true);
8343         this.autoSize();
8344         //this.syncRowHeights(index, index);
8345         //this.layout();
8346         this.fireEvent("rowupdated", this, index, record);
8347     },
8348     
8349     insertRow : function(dm, rowIndex, isUpdate){
8350         
8351         if(!isUpdate){
8352             this.fireEvent("beforerowsinserted", this, rowIndex);
8353         }
8354             //var s = this.getScrollState();
8355         var row = this.renderRow(this.cm, this.store, rowIndex);
8356         // insert before rowIndex..
8357         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8358         
8359         var _this = this;
8360                 
8361         if(row.cellObjects.length){
8362             Roo.each(row.cellObjects, function(r){
8363                 _this.renderCellObject(r);
8364             })
8365         }
8366             
8367         if(!isUpdate){
8368             this.fireEvent("rowsinserted", this, rowIndex);
8369             //this.syncRowHeights(firstRow, lastRow);
8370             //this.stripeRows(firstRow);
8371             //this.layout();
8372         }
8373         
8374     },
8375     
8376     
8377     getRowDom : function(rowIndex)
8378     {
8379         var rows = this.el.select('tbody > tr', true).elements;
8380         
8381         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8382         
8383     },
8384     // returns the object tree for a tr..
8385   
8386     
8387     renderRow : function(cm, ds, rowIndex) 
8388     {
8389         var d = ds.getAt(rowIndex);
8390         
8391         var row = {
8392             tag : 'tr',
8393             cls : 'x-row-' + rowIndex,
8394             cn : []
8395         };
8396             
8397         var cellObjects = [];
8398         
8399         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8400             var config = cm.config[i];
8401             
8402             var renderer = cm.getRenderer(i);
8403             var value = '';
8404             var id = false;
8405             
8406             if(typeof(renderer) !== 'undefined'){
8407                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8408             }
8409             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8410             // and are rendered into the cells after the row is rendered - using the id for the element.
8411             
8412             if(typeof(value) === 'object'){
8413                 id = Roo.id();
8414                 cellObjects.push({
8415                     container : id,
8416                     cfg : value 
8417                 })
8418             }
8419             
8420             var rowcfg = {
8421                 record: d,
8422                 rowIndex : rowIndex,
8423                 colIndex : i,
8424                 rowClass : ''
8425             };
8426
8427             this.fireEvent('rowclass', this, rowcfg);
8428             
8429             var td = {
8430                 tag: 'td',
8431                 cls : rowcfg.rowClass + ' x-col-' + i,
8432                 style: '',
8433                 html: (typeof(value) === 'object') ? '' : value
8434             };
8435             
8436             if (id) {
8437                 td.id = id;
8438             }
8439             
8440             if(typeof(config.colspan) != 'undefined'){
8441                 td.colspan = config.colspan;
8442             }
8443             
8444             if(typeof(config.hidden) != 'undefined' && config.hidden){
8445                 td.style += ' display:none;';
8446             }
8447             
8448             if(typeof(config.align) != 'undefined' && config.align.length){
8449                 td.style += ' text-align:' + config.align + ';';
8450             }
8451             if(typeof(config.valign) != 'undefined' && config.valign.length){
8452                 td.style += ' vertical-align:' + config.valign + ';';
8453             }
8454             
8455             if(typeof(config.width) != 'undefined'){
8456                 td.style += ' width:' +  config.width + 'px;';
8457             }
8458             
8459             if(typeof(config.cursor) != 'undefined'){
8460                 td.style += ' cursor:' +  config.cursor + ';';
8461             }
8462             
8463             if(typeof(config.cls) != 'undefined'){
8464                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8465             }
8466             
8467             ['xs','sm','md','lg'].map(function(size){
8468                 
8469                 if(typeof(config[size]) == 'undefined'){
8470                     return;
8471                 }
8472                 
8473                 
8474                   
8475                 if (!config[size]) { // 0 = hidden
8476                     // BS 4 '0' is treated as hide that column and below.
8477                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8478                     return;
8479                 }
8480                 
8481                 td.cls += ' col-' + size + '-' + config[size] + (
8482                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8483                 );
8484                  
8485
8486             });
8487             
8488             row.cn.push(td);
8489            
8490         }
8491         
8492         row.cellObjects = cellObjects;
8493         
8494         return row;
8495           
8496     },
8497     
8498     
8499     
8500     onBeforeLoad : function()
8501     {
8502         
8503     },
8504      /**
8505      * Remove all rows
8506      */
8507     clear : function()
8508     {
8509         this.el.select('tbody', true).first().dom.innerHTML = '';
8510     },
8511     /**
8512      * Show or hide a row.
8513      * @param {Number} rowIndex to show or hide
8514      * @param {Boolean} state hide
8515      */
8516     setRowVisibility : function(rowIndex, state)
8517     {
8518         var bt = this.mainBody.dom;
8519         
8520         var rows = this.el.select('tbody > tr', true).elements;
8521         
8522         if(typeof(rows[rowIndex]) == 'undefined'){
8523             return;
8524         }
8525         rows[rowIndex].dom.style.display = state ? '' : 'none';
8526     },
8527     
8528     
8529     getSelectionModel : function(){
8530         if(!this.selModel){
8531             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8532         }
8533         return this.selModel;
8534     },
8535     /*
8536      * Render the Roo.bootstrap object from renderder
8537      */
8538     renderCellObject : function(r)
8539     {
8540         var _this = this;
8541         
8542         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8543         
8544         var t = r.cfg.render(r.container);
8545         
8546         if(r.cfg.cn){
8547             Roo.each(r.cfg.cn, function(c){
8548                 var child = {
8549                     container: t.getChildContainer(),
8550                     cfg: c
8551                 };
8552                 _this.renderCellObject(child);
8553             })
8554         }
8555     },
8556     
8557     getRowIndex : function(row)
8558     {
8559         var rowIndex = -1;
8560         
8561         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8562             if(el != row){
8563                 return;
8564             }
8565             
8566             rowIndex = index;
8567         });
8568         
8569         return rowIndex;
8570     },
8571      /**
8572      * Returns the grid's underlying element = used by panel.Grid
8573      * @return {Element} The element
8574      */
8575     getGridEl : function(){
8576         return this.el;
8577     },
8578      /**
8579      * Forces a resize - used by panel.Grid
8580      * @return {Element} The element
8581      */
8582     autoSize : function()
8583     {
8584         //var ctr = Roo.get(this.container.dom.parentElement);
8585         var ctr = Roo.get(this.el.dom);
8586         
8587         var thd = this.getGridEl().select('thead',true).first();
8588         var tbd = this.getGridEl().select('tbody', true).first();
8589         var tfd = this.getGridEl().select('tfoot', true).first();
8590         
8591         var cw = ctr.getWidth();
8592         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8593         
8594         if (tbd) {
8595             
8596             tbd.setWidth(ctr.getWidth());
8597             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8598             // this needs fixing for various usage - currently only hydra job advers I think..
8599             //tdb.setHeight(
8600             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8601             //); 
8602             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8603             cw -= barsize;
8604         }
8605         cw = Math.max(cw, this.totalWidth);
8606         this.getGridEl().select('tbody tr',true).setWidth(cw);
8607         
8608         // resize 'expandable coloumn?
8609         
8610         return; // we doe not have a view in this design..
8611         
8612     },
8613     onBodyScroll: function()
8614     {
8615         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8616         if(this.mainHead){
8617             this.mainHead.setStyle({
8618                 'position' : 'relative',
8619                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8620             });
8621         }
8622         
8623         if(this.lazyLoad){
8624             
8625             var scrollHeight = this.mainBody.dom.scrollHeight;
8626             
8627             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8628             
8629             var height = this.mainBody.getHeight();
8630             
8631             if(scrollHeight - height == scrollTop) {
8632                 
8633                 var total = this.ds.getTotalCount();
8634                 
8635                 if(this.footer.cursor + this.footer.pageSize < total){
8636                     
8637                     this.footer.ds.load({
8638                         params : {
8639                             start : this.footer.cursor + this.footer.pageSize,
8640                             limit : this.footer.pageSize
8641                         },
8642                         add : true
8643                     });
8644                 }
8645             }
8646             
8647         }
8648     },
8649     
8650     onHeaderChange : function()
8651     {
8652         var header = this.renderHeader();
8653         var table = this.el.select('table', true).first();
8654         
8655         this.mainHead.remove();
8656         this.mainHead = table.createChild(header, this.mainBody, false);
8657     },
8658     
8659     onHiddenChange : function(colModel, colIndex, hidden)
8660     {
8661         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8662         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8663         
8664         this.CSS.updateRule(thSelector, "display", "");
8665         this.CSS.updateRule(tdSelector, "display", "");
8666         
8667         if(hidden){
8668             this.CSS.updateRule(thSelector, "display", "none");
8669             this.CSS.updateRule(tdSelector, "display", "none");
8670         }
8671         
8672         this.onHeaderChange();
8673         this.onLoad();
8674     },
8675     
8676     setColumnWidth: function(col_index, width)
8677     {
8678         // width = "md-2 xs-2..."
8679         if(!this.colModel.config[col_index]) {
8680             return;
8681         }
8682         
8683         var w = width.split(" ");
8684         
8685         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8686         
8687         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8688         
8689         
8690         for(var j = 0; j < w.length; j++) {
8691             
8692             if(!w[j]) {
8693                 continue;
8694             }
8695             
8696             var size_cls = w[j].split("-");
8697             
8698             if(!Number.isInteger(size_cls[1] * 1)) {
8699                 continue;
8700             }
8701             
8702             if(!this.colModel.config[col_index][size_cls[0]]) {
8703                 continue;
8704             }
8705             
8706             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8707                 continue;
8708             }
8709             
8710             h_row[0].classList.replace(
8711                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8712                 "col-"+size_cls[0]+"-"+size_cls[1]
8713             );
8714             
8715             for(var i = 0; i < rows.length; i++) {
8716                 
8717                 var size_cls = w[j].split("-");
8718                 
8719                 if(!Number.isInteger(size_cls[1] * 1)) {
8720                     continue;
8721                 }
8722                 
8723                 if(!this.colModel.config[col_index][size_cls[0]]) {
8724                     continue;
8725                 }
8726                 
8727                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8728                     continue;
8729                 }
8730                 
8731                 rows[i].classList.replace(
8732                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8733                     "col-"+size_cls[0]+"-"+size_cls[1]
8734                 );
8735             }
8736             
8737             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8738         }
8739     }
8740 });
8741
8742  
8743
8744  /*
8745  * - LGPL
8746  *
8747  * table cell
8748  * 
8749  */
8750
8751 /**
8752  * @class Roo.bootstrap.TableCell
8753  * @extends Roo.bootstrap.Component
8754  * Bootstrap TableCell class
8755  * @cfg {String} html cell contain text
8756  * @cfg {String} cls cell class
8757  * @cfg {String} tag cell tag (td|th) default td
8758  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8759  * @cfg {String} align Aligns the content in a cell
8760  * @cfg {String} axis Categorizes cells
8761  * @cfg {String} bgcolor Specifies the background color of a cell
8762  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8763  * @cfg {Number} colspan Specifies the number of columns a cell should span
8764  * @cfg {String} headers Specifies one or more header cells a cell is related to
8765  * @cfg {Number} height Sets the height of a cell
8766  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8767  * @cfg {Number} rowspan Sets the number of rows a cell should span
8768  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8769  * @cfg {String} valign Vertical aligns the content in a cell
8770  * @cfg {Number} width Specifies the width of a cell
8771  * 
8772  * @constructor
8773  * Create a new TableCell
8774  * @param {Object} config The config object
8775  */
8776
8777 Roo.bootstrap.TableCell = function(config){
8778     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8779 };
8780
8781 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8782     
8783     html: false,
8784     cls: false,
8785     tag: false,
8786     abbr: false,
8787     align: false,
8788     axis: false,
8789     bgcolor: false,
8790     charoff: false,
8791     colspan: false,
8792     headers: false,
8793     height: false,
8794     nowrap: false,
8795     rowspan: false,
8796     scope: false,
8797     valign: false,
8798     width: false,
8799     
8800     
8801     getAutoCreate : function(){
8802         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8803         
8804         cfg = {
8805             tag: 'td'
8806         };
8807         
8808         if(this.tag){
8809             cfg.tag = this.tag;
8810         }
8811         
8812         if (this.html) {
8813             cfg.html=this.html
8814         }
8815         if (this.cls) {
8816             cfg.cls=this.cls
8817         }
8818         if (this.abbr) {
8819             cfg.abbr=this.abbr
8820         }
8821         if (this.align) {
8822             cfg.align=this.align
8823         }
8824         if (this.axis) {
8825             cfg.axis=this.axis
8826         }
8827         if (this.bgcolor) {
8828             cfg.bgcolor=this.bgcolor
8829         }
8830         if (this.charoff) {
8831             cfg.charoff=this.charoff
8832         }
8833         if (this.colspan) {
8834             cfg.colspan=this.colspan
8835         }
8836         if (this.headers) {
8837             cfg.headers=this.headers
8838         }
8839         if (this.height) {
8840             cfg.height=this.height
8841         }
8842         if (this.nowrap) {
8843             cfg.nowrap=this.nowrap
8844         }
8845         if (this.rowspan) {
8846             cfg.rowspan=this.rowspan
8847         }
8848         if (this.scope) {
8849             cfg.scope=this.scope
8850         }
8851         if (this.valign) {
8852             cfg.valign=this.valign
8853         }
8854         if (this.width) {
8855             cfg.width=this.width
8856         }
8857         
8858         
8859         return cfg;
8860     }
8861    
8862 });
8863
8864  
8865
8866  /*
8867  * - LGPL
8868  *
8869  * table row
8870  * 
8871  */
8872
8873 /**
8874  * @class Roo.bootstrap.TableRow
8875  * @extends Roo.bootstrap.Component
8876  * Bootstrap TableRow class
8877  * @cfg {String} cls row class
8878  * @cfg {String} align Aligns the content in a table row
8879  * @cfg {String} bgcolor Specifies a background color for a table row
8880  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8881  * @cfg {String} valign Vertical aligns the content in a table row
8882  * 
8883  * @constructor
8884  * Create a new TableRow
8885  * @param {Object} config The config object
8886  */
8887
8888 Roo.bootstrap.TableRow = function(config){
8889     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8890 };
8891
8892 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8893     
8894     cls: false,
8895     align: false,
8896     bgcolor: false,
8897     charoff: false,
8898     valign: false,
8899     
8900     getAutoCreate : function(){
8901         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8902         
8903         cfg = {
8904             tag: 'tr'
8905         };
8906             
8907         if(this.cls){
8908             cfg.cls = this.cls;
8909         }
8910         if(this.align){
8911             cfg.align = this.align;
8912         }
8913         if(this.bgcolor){
8914             cfg.bgcolor = this.bgcolor;
8915         }
8916         if(this.charoff){
8917             cfg.charoff = this.charoff;
8918         }
8919         if(this.valign){
8920             cfg.valign = this.valign;
8921         }
8922         
8923         return cfg;
8924     }
8925    
8926 });
8927
8928  
8929
8930  /*
8931  * - LGPL
8932  *
8933  * table body
8934  * 
8935  */
8936
8937 /**
8938  * @class Roo.bootstrap.TableBody
8939  * @extends Roo.bootstrap.Component
8940  * Bootstrap TableBody class
8941  * @cfg {String} cls element class
8942  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8943  * @cfg {String} align Aligns the content inside the element
8944  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8945  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8946  * 
8947  * @constructor
8948  * Create a new TableBody
8949  * @param {Object} config The config object
8950  */
8951
8952 Roo.bootstrap.TableBody = function(config){
8953     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8954 };
8955
8956 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8957     
8958     cls: false,
8959     tag: false,
8960     align: false,
8961     charoff: false,
8962     valign: false,
8963     
8964     getAutoCreate : function(){
8965         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8966         
8967         cfg = {
8968             tag: 'tbody'
8969         };
8970             
8971         if (this.cls) {
8972             cfg.cls=this.cls
8973         }
8974         if(this.tag){
8975             cfg.tag = this.tag;
8976         }
8977         
8978         if(this.align){
8979             cfg.align = this.align;
8980         }
8981         if(this.charoff){
8982             cfg.charoff = this.charoff;
8983         }
8984         if(this.valign){
8985             cfg.valign = this.valign;
8986         }
8987         
8988         return cfg;
8989     }
8990     
8991     
8992 //    initEvents : function()
8993 //    {
8994 //        
8995 //        if(!this.store){
8996 //            return;
8997 //        }
8998 //        
8999 //        this.store = Roo.factory(this.store, Roo.data);
9000 //        this.store.on('load', this.onLoad, this);
9001 //        
9002 //        this.store.load();
9003 //        
9004 //    },
9005 //    
9006 //    onLoad: function () 
9007 //    {   
9008 //        this.fireEvent('load', this);
9009 //    }
9010 //    
9011 //   
9012 });
9013
9014  
9015
9016  /*
9017  * Based on:
9018  * Ext JS Library 1.1.1
9019  * Copyright(c) 2006-2007, Ext JS, LLC.
9020  *
9021  * Originally Released Under LGPL - original licence link has changed is not relivant.
9022  *
9023  * Fork - LGPL
9024  * <script type="text/javascript">
9025  */
9026
9027 // as we use this in bootstrap.
9028 Roo.namespace('Roo.form');
9029  /**
9030  * @class Roo.form.Action
9031  * Internal Class used to handle form actions
9032  * @constructor
9033  * @param {Roo.form.BasicForm} el The form element or its id
9034  * @param {Object} config Configuration options
9035  */
9036
9037  
9038  
9039 // define the action interface
9040 Roo.form.Action = function(form, options){
9041     this.form = form;
9042     this.options = options || {};
9043 };
9044 /**
9045  * Client Validation Failed
9046  * @const 
9047  */
9048 Roo.form.Action.CLIENT_INVALID = 'client';
9049 /**
9050  * Server Validation Failed
9051  * @const 
9052  */
9053 Roo.form.Action.SERVER_INVALID = 'server';
9054  /**
9055  * Connect to Server Failed
9056  * @const 
9057  */
9058 Roo.form.Action.CONNECT_FAILURE = 'connect';
9059 /**
9060  * Reading Data from Server Failed
9061  * @const 
9062  */
9063 Roo.form.Action.LOAD_FAILURE = 'load';
9064
9065 Roo.form.Action.prototype = {
9066     type : 'default',
9067     failureType : undefined,
9068     response : undefined,
9069     result : undefined,
9070
9071     // interface method
9072     run : function(options){
9073
9074     },
9075
9076     // interface method
9077     success : function(response){
9078
9079     },
9080
9081     // interface method
9082     handleResponse : function(response){
9083
9084     },
9085
9086     // default connection failure
9087     failure : function(response){
9088         
9089         this.response = response;
9090         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9091         this.form.afterAction(this, false);
9092     },
9093
9094     processResponse : function(response){
9095         this.response = response;
9096         if(!response.responseText){
9097             return true;
9098         }
9099         this.result = this.handleResponse(response);
9100         return this.result;
9101     },
9102
9103     // utility functions used internally
9104     getUrl : function(appendParams){
9105         var url = this.options.url || this.form.url || this.form.el.dom.action;
9106         if(appendParams){
9107             var p = this.getParams();
9108             if(p){
9109                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9110             }
9111         }
9112         return url;
9113     },
9114
9115     getMethod : function(){
9116         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9117     },
9118
9119     getParams : function(){
9120         var bp = this.form.baseParams;
9121         var p = this.options.params;
9122         if(p){
9123             if(typeof p == "object"){
9124                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9125             }else if(typeof p == 'string' && bp){
9126                 p += '&' + Roo.urlEncode(bp);
9127             }
9128         }else if(bp){
9129             p = Roo.urlEncode(bp);
9130         }
9131         return p;
9132     },
9133
9134     createCallback : function(){
9135         return {
9136             success: this.success,
9137             failure: this.failure,
9138             scope: this,
9139             timeout: (this.form.timeout*1000),
9140             upload: this.form.fileUpload ? this.success : undefined
9141         };
9142     }
9143 };
9144
9145 Roo.form.Action.Submit = function(form, options){
9146     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9147 };
9148
9149 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9150     type : 'submit',
9151
9152     haveProgress : false,
9153     uploadComplete : false,
9154     
9155     // uploadProgress indicator.
9156     uploadProgress : function()
9157     {
9158         if (!this.form.progressUrl) {
9159             return;
9160         }
9161         
9162         if (!this.haveProgress) {
9163             Roo.MessageBox.progress("Uploading", "Uploading");
9164         }
9165         if (this.uploadComplete) {
9166            Roo.MessageBox.hide();
9167            return;
9168         }
9169         
9170         this.haveProgress = true;
9171    
9172         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9173         
9174         var c = new Roo.data.Connection();
9175         c.request({
9176             url : this.form.progressUrl,
9177             params: {
9178                 id : uid
9179             },
9180             method: 'GET',
9181             success : function(req){
9182                //console.log(data);
9183                 var rdata = false;
9184                 var edata;
9185                 try  {
9186                    rdata = Roo.decode(req.responseText)
9187                 } catch (e) {
9188                     Roo.log("Invalid data from server..");
9189                     Roo.log(edata);
9190                     return;
9191                 }
9192                 if (!rdata || !rdata.success) {
9193                     Roo.log(rdata);
9194                     Roo.MessageBox.alert(Roo.encode(rdata));
9195                     return;
9196                 }
9197                 var data = rdata.data;
9198                 
9199                 if (this.uploadComplete) {
9200                    Roo.MessageBox.hide();
9201                    return;
9202                 }
9203                    
9204                 if (data){
9205                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9206                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9207                     );
9208                 }
9209                 this.uploadProgress.defer(2000,this);
9210             },
9211        
9212             failure: function(data) {
9213                 Roo.log('progress url failed ');
9214                 Roo.log(data);
9215             },
9216             scope : this
9217         });
9218            
9219     },
9220     
9221     
9222     run : function()
9223     {
9224         // run get Values on the form, so it syncs any secondary forms.
9225         this.form.getValues();
9226         
9227         var o = this.options;
9228         var method = this.getMethod();
9229         var isPost = method == 'POST';
9230         if(o.clientValidation === false || this.form.isValid()){
9231             
9232             if (this.form.progressUrl) {
9233                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9234                     (new Date() * 1) + '' + Math.random());
9235                     
9236             } 
9237             
9238             
9239             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9240                 form:this.form.el.dom,
9241                 url:this.getUrl(!isPost),
9242                 method: method,
9243                 params:isPost ? this.getParams() : null,
9244                 isUpload: this.form.fileUpload,
9245                 formData : this.form.formData
9246             }));
9247             
9248             this.uploadProgress();
9249
9250         }else if (o.clientValidation !== false){ // client validation failed
9251             this.failureType = Roo.form.Action.CLIENT_INVALID;
9252             this.form.afterAction(this, false);
9253         }
9254     },
9255
9256     success : function(response)
9257     {
9258         this.uploadComplete= true;
9259         if (this.haveProgress) {
9260             Roo.MessageBox.hide();
9261         }
9262         
9263         
9264         var result = this.processResponse(response);
9265         if(result === true || result.success){
9266             this.form.afterAction(this, true);
9267             return;
9268         }
9269         if(result.errors){
9270             this.form.markInvalid(result.errors);
9271             this.failureType = Roo.form.Action.SERVER_INVALID;
9272         }
9273         this.form.afterAction(this, false);
9274     },
9275     failure : function(response)
9276     {
9277         this.uploadComplete= true;
9278         if (this.haveProgress) {
9279             Roo.MessageBox.hide();
9280         }
9281         
9282         this.response = response;
9283         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9284         this.form.afterAction(this, false);
9285     },
9286     
9287     handleResponse : function(response){
9288         if(this.form.errorReader){
9289             var rs = this.form.errorReader.read(response);
9290             var errors = [];
9291             if(rs.records){
9292                 for(var i = 0, len = rs.records.length; i < len; i++) {
9293                     var r = rs.records[i];
9294                     errors[i] = r.data;
9295                 }
9296             }
9297             if(errors.length < 1){
9298                 errors = null;
9299             }
9300             return {
9301                 success : rs.success,
9302                 errors : errors
9303             };
9304         }
9305         var ret = false;
9306         try {
9307             ret = Roo.decode(response.responseText);
9308         } catch (e) {
9309             ret = {
9310                 success: false,
9311                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9312                 errors : []
9313             };
9314         }
9315         return ret;
9316         
9317     }
9318 });
9319
9320
9321 Roo.form.Action.Load = function(form, options){
9322     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9323     this.reader = this.form.reader;
9324 };
9325
9326 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9327     type : 'load',
9328
9329     run : function(){
9330         
9331         Roo.Ajax.request(Roo.apply(
9332                 this.createCallback(), {
9333                     method:this.getMethod(),
9334                     url:this.getUrl(false),
9335                     params:this.getParams()
9336         }));
9337     },
9338
9339     success : function(response){
9340         
9341         var result = this.processResponse(response);
9342         if(result === true || !result.success || !result.data){
9343             this.failureType = Roo.form.Action.LOAD_FAILURE;
9344             this.form.afterAction(this, false);
9345             return;
9346         }
9347         this.form.clearInvalid();
9348         this.form.setValues(result.data);
9349         this.form.afterAction(this, true);
9350     },
9351
9352     handleResponse : function(response){
9353         if(this.form.reader){
9354             var rs = this.form.reader.read(response);
9355             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9356             return {
9357                 success : rs.success,
9358                 data : data
9359             };
9360         }
9361         return Roo.decode(response.responseText);
9362     }
9363 });
9364
9365 Roo.form.Action.ACTION_TYPES = {
9366     'load' : Roo.form.Action.Load,
9367     'submit' : Roo.form.Action.Submit
9368 };/*
9369  * - LGPL
9370  *
9371  * form
9372  *
9373  */
9374
9375 /**
9376  * @class Roo.bootstrap.Form
9377  * @extends Roo.bootstrap.Component
9378  * Bootstrap Form class
9379  * @cfg {String} method  GET | POST (default POST)
9380  * @cfg {String} labelAlign top | left (default top)
9381  * @cfg {String} align left  | right - for navbars
9382  * @cfg {Boolean} loadMask load mask when submit (default true)
9383
9384  *
9385  * @constructor
9386  * Create a new Form
9387  * @param {Object} config The config object
9388  */
9389
9390
9391 Roo.bootstrap.Form = function(config){
9392     
9393     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9394     
9395     Roo.bootstrap.Form.popover.apply();
9396     
9397     this.addEvents({
9398         /**
9399          * @event clientvalidation
9400          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9401          * @param {Form} this
9402          * @param {Boolean} valid true if the form has passed client-side validation
9403          */
9404         clientvalidation: true,
9405         /**
9406          * @event beforeaction
9407          * Fires before any action is performed. Return false to cancel the action.
9408          * @param {Form} this
9409          * @param {Action} action The action to be performed
9410          */
9411         beforeaction: true,
9412         /**
9413          * @event actionfailed
9414          * Fires when an action fails.
9415          * @param {Form} this
9416          * @param {Action} action The action that failed
9417          */
9418         actionfailed : true,
9419         /**
9420          * @event actioncomplete
9421          * Fires when an action is completed.
9422          * @param {Form} this
9423          * @param {Action} action The action that completed
9424          */
9425         actioncomplete : true
9426     });
9427 };
9428
9429 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9430
9431      /**
9432      * @cfg {String} method
9433      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9434      */
9435     method : 'POST',
9436     /**
9437      * @cfg {String} url
9438      * The URL to use for form actions if one isn't supplied in the action options.
9439      */
9440     /**
9441      * @cfg {Boolean} fileUpload
9442      * Set to true if this form is a file upload.
9443      */
9444
9445     /**
9446      * @cfg {Object} baseParams
9447      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9448      */
9449
9450     /**
9451      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9452      */
9453     timeout: 30,
9454     /**
9455      * @cfg {Sting} align (left|right) for navbar forms
9456      */
9457     align : 'left',
9458
9459     // private
9460     activeAction : null,
9461
9462     /**
9463      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9464      * element by passing it or its id or mask the form itself by passing in true.
9465      * @type Mixed
9466      */
9467     waitMsgTarget : false,
9468
9469     loadMask : true,
9470     
9471     /**
9472      * @cfg {Boolean} errorMask (true|false) default false
9473      */
9474     errorMask : false,
9475     
9476     /**
9477      * @cfg {Number} maskOffset Default 100
9478      */
9479     maskOffset : 100,
9480     
9481     /**
9482      * @cfg {Boolean} maskBody
9483      */
9484     maskBody : false,
9485
9486     getAutoCreate : function(){
9487
9488         var cfg = {
9489             tag: 'form',
9490             method : this.method || 'POST',
9491             id : this.id || Roo.id(),
9492             cls : ''
9493         };
9494         if (this.parent().xtype.match(/^Nav/)) {
9495             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9496
9497         }
9498
9499         if (this.labelAlign == 'left' ) {
9500             cfg.cls += ' form-horizontal';
9501         }
9502
9503
9504         return cfg;
9505     },
9506     initEvents : function()
9507     {
9508         this.el.on('submit', this.onSubmit, this);
9509         // this was added as random key presses on the form where triggering form submit.
9510         this.el.on('keypress', function(e) {
9511             if (e.getCharCode() != 13) {
9512                 return true;
9513             }
9514             // we might need to allow it for textareas.. and some other items.
9515             // check e.getTarget().
9516
9517             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9518                 return true;
9519             }
9520
9521             Roo.log("keypress blocked");
9522
9523             e.preventDefault();
9524             return false;
9525         });
9526         
9527     },
9528     // private
9529     onSubmit : function(e){
9530         e.stopEvent();
9531     },
9532
9533      /**
9534      * Returns true if client-side validation on the form is successful.
9535      * @return Boolean
9536      */
9537     isValid : function(){
9538         var items = this.getItems();
9539         var valid = true;
9540         var target = false;
9541         
9542         items.each(function(f){
9543             
9544             if(f.validate()){
9545                 return;
9546             }
9547             
9548             Roo.log('invalid field: ' + f.name);
9549             
9550             valid = false;
9551
9552             if(!target && f.el.isVisible(true)){
9553                 target = f;
9554             }
9555            
9556         });
9557         
9558         if(this.errorMask && !valid){
9559             Roo.bootstrap.Form.popover.mask(this, target);
9560         }
9561         
9562         return valid;
9563     },
9564     
9565     /**
9566      * Returns true if any fields in this form have changed since their original load.
9567      * @return Boolean
9568      */
9569     isDirty : function(){
9570         var dirty = false;
9571         var items = this.getItems();
9572         items.each(function(f){
9573            if(f.isDirty()){
9574                dirty = true;
9575                return false;
9576            }
9577            return true;
9578         });
9579         return dirty;
9580     },
9581      /**
9582      * Performs a predefined action (submit or load) or custom actions you define on this form.
9583      * @param {String} actionName The name of the action type
9584      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9585      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9586      * accept other config options):
9587      * <pre>
9588 Property          Type             Description
9589 ----------------  ---------------  ----------------------------------------------------------------------------------
9590 url               String           The url for the action (defaults to the form's url)
9591 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9592 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9593 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9594                                    validate the form on the client (defaults to false)
9595      * </pre>
9596      * @return {BasicForm} this
9597      */
9598     doAction : function(action, options){
9599         if(typeof action == 'string'){
9600             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9601         }
9602         if(this.fireEvent('beforeaction', this, action) !== false){
9603             this.beforeAction(action);
9604             action.run.defer(100, action);
9605         }
9606         return this;
9607     },
9608
9609     // private
9610     beforeAction : function(action){
9611         var o = action.options;
9612         
9613         if(this.loadMask){
9614             
9615             if(this.maskBody){
9616                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9617             } else {
9618                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9619             }
9620         }
9621         // not really supported yet.. ??
9622
9623         //if(this.waitMsgTarget === true){
9624         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9625         //}else if(this.waitMsgTarget){
9626         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9627         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9628         //}else {
9629         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9630        // }
9631
9632     },
9633
9634     // private
9635     afterAction : function(action, success){
9636         this.activeAction = null;
9637         var o = action.options;
9638
9639         if(this.loadMask){
9640             
9641             if(this.maskBody){
9642                 Roo.get(document.body).unmask();
9643             } else {
9644                 this.el.unmask();
9645             }
9646         }
9647         
9648         //if(this.waitMsgTarget === true){
9649 //            this.el.unmask();
9650         //}else if(this.waitMsgTarget){
9651         //    this.waitMsgTarget.unmask();
9652         //}else{
9653         //    Roo.MessageBox.updateProgress(1);
9654         //    Roo.MessageBox.hide();
9655        // }
9656         //
9657         if(success){
9658             if(o.reset){
9659                 this.reset();
9660             }
9661             Roo.callback(o.success, o.scope, [this, action]);
9662             this.fireEvent('actioncomplete', this, action);
9663
9664         }else{
9665
9666             // failure condition..
9667             // we have a scenario where updates need confirming.
9668             // eg. if a locking scenario exists..
9669             // we look for { errors : { needs_confirm : true }} in the response.
9670             if (
9671                 (typeof(action.result) != 'undefined')  &&
9672                 (typeof(action.result.errors) != 'undefined')  &&
9673                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9674            ){
9675                 var _t = this;
9676                 Roo.log("not supported yet");
9677                  /*
9678
9679                 Roo.MessageBox.confirm(
9680                     "Change requires confirmation",
9681                     action.result.errorMsg,
9682                     function(r) {
9683                         if (r != 'yes') {
9684                             return;
9685                         }
9686                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9687                     }
9688
9689                 );
9690                 */
9691
9692
9693                 return;
9694             }
9695
9696             Roo.callback(o.failure, o.scope, [this, action]);
9697             // show an error message if no failed handler is set..
9698             if (!this.hasListener('actionfailed')) {
9699                 Roo.log("need to add dialog support");
9700                 /*
9701                 Roo.MessageBox.alert("Error",
9702                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9703                         action.result.errorMsg :
9704                         "Saving Failed, please check your entries or try again"
9705                 );
9706                 */
9707             }
9708
9709             this.fireEvent('actionfailed', this, action);
9710         }
9711
9712     },
9713     /**
9714      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9715      * @param {String} id The value to search for
9716      * @return Field
9717      */
9718     findField : function(id){
9719         var items = this.getItems();
9720         var field = items.get(id);
9721         if(!field){
9722              items.each(function(f){
9723                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9724                     field = f;
9725                     return false;
9726                 }
9727                 return true;
9728             });
9729         }
9730         return field || null;
9731     },
9732      /**
9733      * Mark fields in this form invalid in bulk.
9734      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9735      * @return {BasicForm} this
9736      */
9737     markInvalid : function(errors){
9738         if(errors instanceof Array){
9739             for(var i = 0, len = errors.length; i < len; i++){
9740                 var fieldError = errors[i];
9741                 var f = this.findField(fieldError.id);
9742                 if(f){
9743                     f.markInvalid(fieldError.msg);
9744                 }
9745             }
9746         }else{
9747             var field, id;
9748             for(id in errors){
9749                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9750                     field.markInvalid(errors[id]);
9751                 }
9752             }
9753         }
9754         //Roo.each(this.childForms || [], function (f) {
9755         //    f.markInvalid(errors);
9756         //});
9757
9758         return this;
9759     },
9760
9761     /**
9762      * Set values for fields in this form in bulk.
9763      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9764      * @return {BasicForm} this
9765      */
9766     setValues : function(values){
9767         if(values instanceof Array){ // array of objects
9768             for(var i = 0, len = values.length; i < len; i++){
9769                 var v = values[i];
9770                 var f = this.findField(v.id);
9771                 if(f){
9772                     f.setValue(v.value);
9773                     if(this.trackResetOnLoad){
9774                         f.originalValue = f.getValue();
9775                     }
9776                 }
9777             }
9778         }else{ // object hash
9779             var field, id;
9780             for(id in values){
9781                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9782
9783                     if (field.setFromData &&
9784                         field.valueField &&
9785                         field.displayField &&
9786                         // combos' with local stores can
9787                         // be queried via setValue()
9788                         // to set their value..
9789                         (field.store && !field.store.isLocal)
9790                         ) {
9791                         // it's a combo
9792                         var sd = { };
9793                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9794                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9795                         field.setFromData(sd);
9796
9797                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9798                         
9799                         field.setFromData(values);
9800                         
9801                     } else {
9802                         field.setValue(values[id]);
9803                     }
9804
9805
9806                     if(this.trackResetOnLoad){
9807                         field.originalValue = field.getValue();
9808                     }
9809                 }
9810             }
9811         }
9812
9813         //Roo.each(this.childForms || [], function (f) {
9814         //    f.setValues(values);
9815         //});
9816
9817         return this;
9818     },
9819
9820     /**
9821      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9822      * they are returned as an array.
9823      * @param {Boolean} asString
9824      * @return {Object}
9825      */
9826     getValues : function(asString){
9827         //if (this.childForms) {
9828             // copy values from the child forms
9829         //    Roo.each(this.childForms, function (f) {
9830         //        this.setValues(f.getValues());
9831         //    }, this);
9832         //}
9833
9834
9835
9836         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9837         if(asString === true){
9838             return fs;
9839         }
9840         return Roo.urlDecode(fs);
9841     },
9842
9843     /**
9844      * Returns the fields in this form as an object with key/value pairs.
9845      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9846      * @return {Object}
9847      */
9848     getFieldValues : function(with_hidden)
9849     {
9850         var items = this.getItems();
9851         var ret = {};
9852         items.each(function(f){
9853             
9854             if (!f.getName()) {
9855                 return;
9856             }
9857             
9858             var v = f.getValue();
9859             
9860             if (f.inputType =='radio') {
9861                 if (typeof(ret[f.getName()]) == 'undefined') {
9862                     ret[f.getName()] = ''; // empty..
9863                 }
9864
9865                 if (!f.el.dom.checked) {
9866                     return;
9867
9868                 }
9869                 v = f.el.dom.value;
9870
9871             }
9872             
9873             if(f.xtype == 'MoneyField'){
9874                 ret[f.currencyName] = f.getCurrency();
9875             }
9876
9877             // not sure if this supported any more..
9878             if ((typeof(v) == 'object') && f.getRawValue) {
9879                 v = f.getRawValue() ; // dates..
9880             }
9881             // combo boxes where name != hiddenName...
9882             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9883                 ret[f.name] = f.getRawValue();
9884             }
9885             ret[f.getName()] = v;
9886         });
9887
9888         return ret;
9889     },
9890
9891     /**
9892      * Clears all invalid messages in this form.
9893      * @return {BasicForm} this
9894      */
9895     clearInvalid : function(){
9896         var items = this.getItems();
9897
9898         items.each(function(f){
9899            f.clearInvalid();
9900         });
9901
9902         return this;
9903     },
9904
9905     /**
9906      * Resets this form.
9907      * @return {BasicForm} this
9908      */
9909     reset : function(){
9910         var items = this.getItems();
9911         items.each(function(f){
9912             f.reset();
9913         });
9914
9915         Roo.each(this.childForms || [], function (f) {
9916             f.reset();
9917         });
9918
9919
9920         return this;
9921     },
9922     
9923     getItems : function()
9924     {
9925         var r=new Roo.util.MixedCollection(false, function(o){
9926             return o.id || (o.id = Roo.id());
9927         });
9928         var iter = function(el) {
9929             if (el.inputEl) {
9930                 r.add(el);
9931             }
9932             if (!el.items) {
9933                 return;
9934             }
9935             Roo.each(el.items,function(e) {
9936                 iter(e);
9937             });
9938         };
9939
9940         iter(this);
9941         return r;
9942     },
9943     
9944     hideFields : function(items)
9945     {
9946         Roo.each(items, function(i){
9947             
9948             var f = this.findField(i);
9949             
9950             if(!f){
9951                 return;
9952             }
9953             
9954             f.hide();
9955             
9956         }, this);
9957     },
9958     
9959     showFields : function(items)
9960     {
9961         Roo.each(items, function(i){
9962             
9963             var f = this.findField(i);
9964             
9965             if(!f){
9966                 return;
9967             }
9968             
9969             f.show();
9970             
9971         }, this);
9972     }
9973
9974 });
9975
9976 Roo.apply(Roo.bootstrap.Form, {
9977     
9978     popover : {
9979         
9980         padding : 5,
9981         
9982         isApplied : false,
9983         
9984         isMasked : false,
9985         
9986         form : false,
9987         
9988         target : false,
9989         
9990         toolTip : false,
9991         
9992         intervalID : false,
9993         
9994         maskEl : false,
9995         
9996         apply : function()
9997         {
9998             if(this.isApplied){
9999                 return;
10000             }
10001             
10002             this.maskEl = {
10003                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10004                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10005                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10006                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10007             };
10008             
10009             this.maskEl.top.enableDisplayMode("block");
10010             this.maskEl.left.enableDisplayMode("block");
10011             this.maskEl.bottom.enableDisplayMode("block");
10012             this.maskEl.right.enableDisplayMode("block");
10013             
10014             this.toolTip = new Roo.bootstrap.Tooltip({
10015                 cls : 'roo-form-error-popover',
10016                 alignment : {
10017                     'left' : ['r-l', [-2,0], 'right'],
10018                     'right' : ['l-r', [2,0], 'left'],
10019                     'bottom' : ['tl-bl', [0,2], 'top'],
10020                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10021                 }
10022             });
10023             
10024             this.toolTip.render(Roo.get(document.body));
10025
10026             this.toolTip.el.enableDisplayMode("block");
10027             
10028             Roo.get(document.body).on('click', function(){
10029                 this.unmask();
10030             }, this);
10031             
10032             Roo.get(document.body).on('touchstart', function(){
10033                 this.unmask();
10034             }, this);
10035             
10036             this.isApplied = true
10037         },
10038         
10039         mask : function(form, target)
10040         {
10041             this.form = form;
10042             
10043             this.target = target;
10044             
10045             if(!this.form.errorMask || !target.el){
10046                 return;
10047             }
10048             
10049             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10050             
10051             Roo.log(scrollable);
10052             
10053             var ot = this.target.el.calcOffsetsTo(scrollable);
10054             
10055             var scrollTo = ot[1] - this.form.maskOffset;
10056             
10057             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10058             
10059             scrollable.scrollTo('top', scrollTo);
10060             
10061             var box = this.target.el.getBox();
10062             Roo.log(box);
10063             var zIndex = Roo.bootstrap.Modal.zIndex++;
10064
10065             
10066             this.maskEl.top.setStyle('position', 'absolute');
10067             this.maskEl.top.setStyle('z-index', zIndex);
10068             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10069             this.maskEl.top.setLeft(0);
10070             this.maskEl.top.setTop(0);
10071             this.maskEl.top.show();
10072             
10073             this.maskEl.left.setStyle('position', 'absolute');
10074             this.maskEl.left.setStyle('z-index', zIndex);
10075             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10076             this.maskEl.left.setLeft(0);
10077             this.maskEl.left.setTop(box.y - this.padding);
10078             this.maskEl.left.show();
10079
10080             this.maskEl.bottom.setStyle('position', 'absolute');
10081             this.maskEl.bottom.setStyle('z-index', zIndex);
10082             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10083             this.maskEl.bottom.setLeft(0);
10084             this.maskEl.bottom.setTop(box.bottom + this.padding);
10085             this.maskEl.bottom.show();
10086
10087             this.maskEl.right.setStyle('position', 'absolute');
10088             this.maskEl.right.setStyle('z-index', zIndex);
10089             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10090             this.maskEl.right.setLeft(box.right + this.padding);
10091             this.maskEl.right.setTop(box.y - this.padding);
10092             this.maskEl.right.show();
10093
10094             this.toolTip.bindEl = this.target.el;
10095
10096             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10097
10098             var tip = this.target.blankText;
10099
10100             if(this.target.getValue() !== '' ) {
10101                 
10102                 if (this.target.invalidText.length) {
10103                     tip = this.target.invalidText;
10104                 } else if (this.target.regexText.length){
10105                     tip = this.target.regexText;
10106                 }
10107             }
10108
10109             this.toolTip.show(tip);
10110
10111             this.intervalID = window.setInterval(function() {
10112                 Roo.bootstrap.Form.popover.unmask();
10113             }, 10000);
10114
10115             window.onwheel = function(){ return false;};
10116             
10117             (function(){ this.isMasked = true; }).defer(500, this);
10118             
10119         },
10120         
10121         unmask : function()
10122         {
10123             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10124                 return;
10125             }
10126             
10127             this.maskEl.top.setStyle('position', 'absolute');
10128             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10129             this.maskEl.top.hide();
10130
10131             this.maskEl.left.setStyle('position', 'absolute');
10132             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10133             this.maskEl.left.hide();
10134
10135             this.maskEl.bottom.setStyle('position', 'absolute');
10136             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10137             this.maskEl.bottom.hide();
10138
10139             this.maskEl.right.setStyle('position', 'absolute');
10140             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10141             this.maskEl.right.hide();
10142             
10143             this.toolTip.hide();
10144             
10145             this.toolTip.el.hide();
10146             
10147             window.onwheel = function(){ return true;};
10148             
10149             if(this.intervalID){
10150                 window.clearInterval(this.intervalID);
10151                 this.intervalID = false;
10152             }
10153             
10154             this.isMasked = false;
10155             
10156         }
10157         
10158     }
10159     
10160 });
10161
10162 /*
10163  * Based on:
10164  * Ext JS Library 1.1.1
10165  * Copyright(c) 2006-2007, Ext JS, LLC.
10166  *
10167  * Originally Released Under LGPL - original licence link has changed is not relivant.
10168  *
10169  * Fork - LGPL
10170  * <script type="text/javascript">
10171  */
10172 /**
10173  * @class Roo.form.VTypes
10174  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10175  * @singleton
10176  */
10177 Roo.form.VTypes = function(){
10178     // closure these in so they are only created once.
10179     var alpha = /^[a-zA-Z_]+$/;
10180     var alphanum = /^[a-zA-Z0-9_]+$/;
10181     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10182     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10183
10184     // All these messages and functions are configurable
10185     return {
10186         /**
10187          * The function used to validate email addresses
10188          * @param {String} value The email address
10189          */
10190         'email' : function(v){
10191             return email.test(v);
10192         },
10193         /**
10194          * The error text to display when the email validation function returns false
10195          * @type String
10196          */
10197         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10198         /**
10199          * The keystroke filter mask to be applied on email input
10200          * @type RegExp
10201          */
10202         'emailMask' : /[a-z0-9_\.\-@]/i,
10203
10204         /**
10205          * The function used to validate URLs
10206          * @param {String} value The URL
10207          */
10208         'url' : function(v){
10209             return url.test(v);
10210         },
10211         /**
10212          * The error text to display when the url validation function returns false
10213          * @type String
10214          */
10215         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10216         
10217         /**
10218          * The function used to validate alpha values
10219          * @param {String} value The value
10220          */
10221         'alpha' : function(v){
10222             return alpha.test(v);
10223         },
10224         /**
10225          * The error text to display when the alpha validation function returns false
10226          * @type String
10227          */
10228         'alphaText' : 'This field should only contain letters and _',
10229         /**
10230          * The keystroke filter mask to be applied on alpha input
10231          * @type RegExp
10232          */
10233         'alphaMask' : /[a-z_]/i,
10234
10235         /**
10236          * The function used to validate alphanumeric values
10237          * @param {String} value The value
10238          */
10239         'alphanum' : function(v){
10240             return alphanum.test(v);
10241         },
10242         /**
10243          * The error text to display when the alphanumeric validation function returns false
10244          * @type String
10245          */
10246         'alphanumText' : 'This field should only contain letters, numbers and _',
10247         /**
10248          * The keystroke filter mask to be applied on alphanumeric input
10249          * @type RegExp
10250          */
10251         'alphanumMask' : /[a-z0-9_]/i
10252     };
10253 }();/*
10254  * - LGPL
10255  *
10256  * Input
10257  * 
10258  */
10259
10260 /**
10261  * @class Roo.bootstrap.Input
10262  * @extends Roo.bootstrap.Component
10263  * Bootstrap Input class
10264  * @cfg {Boolean} disabled is it disabled
10265  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10266  * @cfg {String} name name of the input
10267  * @cfg {string} fieldLabel - the label associated
10268  * @cfg {string} placeholder - placeholder to put in text.
10269  * @cfg {string}  before - input group add on before
10270  * @cfg {string} after - input group add on after
10271  * @cfg {string} size - (lg|sm) or leave empty..
10272  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10273  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10274  * @cfg {Number} md colspan out of 12 for computer-sized screens
10275  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10276  * @cfg {string} value default value of the input
10277  * @cfg {Number} labelWidth set the width of label 
10278  * @cfg {Number} labellg set the width of label (1-12)
10279  * @cfg {Number} labelmd set the width of label (1-12)
10280  * @cfg {Number} labelsm set the width of label (1-12)
10281  * @cfg {Number} labelxs set the width of label (1-12)
10282  * @cfg {String} labelAlign (top|left)
10283  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10284  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10285  * @cfg {String} indicatorpos (left|right) default left
10286  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10287  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10288  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10289
10290  * @cfg {String} align (left|center|right) Default left
10291  * @cfg {Boolean} forceFeedback (true|false) Default false
10292  * 
10293  * @constructor
10294  * Create a new Input
10295  * @param {Object} config The config object
10296  */
10297
10298 Roo.bootstrap.Input = function(config){
10299     
10300     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10301     
10302     this.addEvents({
10303         /**
10304          * @event focus
10305          * Fires when this field receives input focus.
10306          * @param {Roo.form.Field} this
10307          */
10308         focus : true,
10309         /**
10310          * @event blur
10311          * Fires when this field loses input focus.
10312          * @param {Roo.form.Field} this
10313          */
10314         blur : true,
10315         /**
10316          * @event specialkey
10317          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10318          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10319          * @param {Roo.form.Field} this
10320          * @param {Roo.EventObject} e The event object
10321          */
10322         specialkey : true,
10323         /**
10324          * @event change
10325          * Fires just before the field blurs if the field value has changed.
10326          * @param {Roo.form.Field} this
10327          * @param {Mixed} newValue The new value
10328          * @param {Mixed} oldValue The original value
10329          */
10330         change : true,
10331         /**
10332          * @event invalid
10333          * Fires after the field has been marked as invalid.
10334          * @param {Roo.form.Field} this
10335          * @param {String} msg The validation message
10336          */
10337         invalid : true,
10338         /**
10339          * @event valid
10340          * Fires after the field has been validated with no errors.
10341          * @param {Roo.form.Field} this
10342          */
10343         valid : true,
10344          /**
10345          * @event keyup
10346          * Fires after the key up
10347          * @param {Roo.form.Field} this
10348          * @param {Roo.EventObject}  e The event Object
10349          */
10350         keyup : true
10351     });
10352 };
10353
10354 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10355      /**
10356      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10357       automatic validation (defaults to "keyup").
10358      */
10359     validationEvent : "keyup",
10360      /**
10361      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10362      */
10363     validateOnBlur : true,
10364     /**
10365      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10366      */
10367     validationDelay : 250,
10368      /**
10369      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10370      */
10371     focusClass : "x-form-focus",  // not needed???
10372     
10373        
10374     /**
10375      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10376      */
10377     invalidClass : "has-warning",
10378     
10379     /**
10380      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10381      */
10382     validClass : "has-success",
10383     
10384     /**
10385      * @cfg {Boolean} hasFeedback (true|false) default true
10386      */
10387     hasFeedback : true,
10388     
10389     /**
10390      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10391      */
10392     invalidFeedbackClass : "glyphicon-warning-sign",
10393     
10394     /**
10395      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10396      */
10397     validFeedbackClass : "glyphicon-ok",
10398     
10399     /**
10400      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10401      */
10402     selectOnFocus : false,
10403     
10404      /**
10405      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10406      */
10407     maskRe : null,
10408        /**
10409      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10410      */
10411     vtype : null,
10412     
10413       /**
10414      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10415      */
10416     disableKeyFilter : false,
10417     
10418        /**
10419      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10420      */
10421     disabled : false,
10422      /**
10423      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10424      */
10425     allowBlank : true,
10426     /**
10427      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10428      */
10429     blankText : "Please complete this mandatory field",
10430     
10431      /**
10432      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10433      */
10434     minLength : 0,
10435     /**
10436      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10437      */
10438     maxLength : Number.MAX_VALUE,
10439     /**
10440      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10441      */
10442     minLengthText : "The minimum length for this field is {0}",
10443     /**
10444      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10445      */
10446     maxLengthText : "The maximum length for this field is {0}",
10447   
10448     
10449     /**
10450      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10451      * If available, this function will be called only after the basic validators all return true, and will be passed the
10452      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10453      */
10454     validator : null,
10455     /**
10456      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10457      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10458      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10459      */
10460     regex : null,
10461     /**
10462      * @cfg {String} regexText -- Depricated - use Invalid Text
10463      */
10464     regexText : "",
10465     
10466     /**
10467      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10468      */
10469     invalidText : "",
10470     
10471     
10472     
10473     autocomplete: false,
10474     
10475     
10476     fieldLabel : '',
10477     inputType : 'text',
10478     
10479     name : false,
10480     placeholder: false,
10481     before : false,
10482     after : false,
10483     size : false,
10484     hasFocus : false,
10485     preventMark: false,
10486     isFormField : true,
10487     value : '',
10488     labelWidth : 2,
10489     labelAlign : false,
10490     readOnly : false,
10491     align : false,
10492     formatedValue : false,
10493     forceFeedback : false,
10494     
10495     indicatorpos : 'left',
10496     
10497     labellg : 0,
10498     labelmd : 0,
10499     labelsm : 0,
10500     labelxs : 0,
10501     
10502     capture : '',
10503     accept : '',
10504     
10505     parentLabelAlign : function()
10506     {
10507         var parent = this;
10508         while (parent.parent()) {
10509             parent = parent.parent();
10510             if (typeof(parent.labelAlign) !='undefined') {
10511                 return parent.labelAlign;
10512             }
10513         }
10514         return 'left';
10515         
10516     },
10517     
10518     getAutoCreate : function()
10519     {
10520         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10521         
10522         var id = Roo.id();
10523         
10524         var cfg = {};
10525         
10526         if(this.inputType != 'hidden'){
10527             cfg.cls = 'form-group' //input-group
10528         }
10529         
10530         var input =  {
10531             tag: 'input',
10532             id : id,
10533             type : this.inputType,
10534             value : this.value,
10535             cls : 'form-control',
10536             placeholder : this.placeholder || '',
10537             autocomplete : this.autocomplete || 'new-password'
10538         };
10539         if (this.inputType == 'file') {
10540             input.style = 'overflow:hidden'; // why not in CSS?
10541         }
10542         
10543         if(this.capture.length){
10544             input.capture = this.capture;
10545         }
10546         
10547         if(this.accept.length){
10548             input.accept = this.accept + "/*";
10549         }
10550         
10551         if(this.align){
10552             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10553         }
10554         
10555         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10556             input.maxLength = this.maxLength;
10557         }
10558         
10559         if (this.disabled) {
10560             input.disabled=true;
10561         }
10562         
10563         if (this.readOnly) {
10564             input.readonly=true;
10565         }
10566         
10567         if (this.name) {
10568             input.name = this.name;
10569         }
10570         
10571         if (this.size) {
10572             input.cls += ' input-' + this.size;
10573         }
10574         
10575         var settings=this;
10576         ['xs','sm','md','lg'].map(function(size){
10577             if (settings[size]) {
10578                 cfg.cls += ' col-' + size + '-' + settings[size];
10579             }
10580         });
10581         
10582         var inputblock = input;
10583         
10584         var feedback = {
10585             tag: 'span',
10586             cls: 'glyphicon form-control-feedback'
10587         };
10588             
10589         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10590             
10591             inputblock = {
10592                 cls : 'has-feedback',
10593                 cn :  [
10594                     input,
10595                     feedback
10596                 ] 
10597             };  
10598         }
10599         
10600         if (this.before || this.after) {
10601             
10602             inputblock = {
10603                 cls : 'input-group',
10604                 cn :  [] 
10605             };
10606             
10607             if (this.before && typeof(this.before) == 'string') {
10608                 
10609                 inputblock.cn.push({
10610                     tag :'span',
10611                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10612                     html : this.before
10613                 });
10614             }
10615             if (this.before && typeof(this.before) == 'object') {
10616                 this.before = Roo.factory(this.before);
10617                 
10618                 inputblock.cn.push({
10619                     tag :'span',
10620                     cls : 'roo-input-before input-group-prepend   input-group-' +
10621                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10622                 });
10623             }
10624             
10625             inputblock.cn.push(input);
10626             
10627             if (this.after && typeof(this.after) == 'string') {
10628                 inputblock.cn.push({
10629                     tag :'span',
10630                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10631                     html : this.after
10632                 });
10633             }
10634             if (this.after && typeof(this.after) == 'object') {
10635                 this.after = Roo.factory(this.after);
10636                 
10637                 inputblock.cn.push({
10638                     tag :'span',
10639                     cls : 'roo-input-after input-group-append  input-group-' +
10640                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10641                 });
10642             }
10643             
10644             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10645                 inputblock.cls += ' has-feedback';
10646                 inputblock.cn.push(feedback);
10647             }
10648         };
10649         var indicator = {
10650             tag : 'i',
10651             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10652             tooltip : 'This field is required'
10653         };
10654         if (this.allowBlank ) {
10655             indicator.style = this.allowBlank ? ' display:none' : '';
10656         }
10657         if (align ==='left' && this.fieldLabel.length) {
10658             
10659             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10660             
10661             cfg.cn = [
10662                 indicator,
10663                 {
10664                     tag: 'label',
10665                     'for' :  id,
10666                     cls : 'control-label col-form-label',
10667                     html : this.fieldLabel
10668
10669                 },
10670                 {
10671                     cls : "", 
10672                     cn: [
10673                         inputblock
10674                     ]
10675                 }
10676             ];
10677             
10678             var labelCfg = cfg.cn[1];
10679             var contentCfg = cfg.cn[2];
10680             
10681             if(this.indicatorpos == 'right'){
10682                 cfg.cn = [
10683                     {
10684                         tag: 'label',
10685                         'for' :  id,
10686                         cls : 'control-label col-form-label',
10687                         cn : [
10688                             {
10689                                 tag : 'span',
10690                                 html : this.fieldLabel
10691                             },
10692                             indicator
10693                         ]
10694                     },
10695                     {
10696                         cls : "",
10697                         cn: [
10698                             inputblock
10699                         ]
10700                     }
10701
10702                 ];
10703                 
10704                 labelCfg = cfg.cn[0];
10705                 contentCfg = cfg.cn[1];
10706             
10707             }
10708             
10709             if(this.labelWidth > 12){
10710                 labelCfg.style = "width: " + this.labelWidth + 'px';
10711             }
10712             
10713             if(this.labelWidth < 13 && this.labelmd == 0){
10714                 this.labelmd = this.labelWidth;
10715             }
10716             
10717             if(this.labellg > 0){
10718                 labelCfg.cls += ' col-lg-' + this.labellg;
10719                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10720             }
10721             
10722             if(this.labelmd > 0){
10723                 labelCfg.cls += ' col-md-' + this.labelmd;
10724                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10725             }
10726             
10727             if(this.labelsm > 0){
10728                 labelCfg.cls += ' col-sm-' + this.labelsm;
10729                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10730             }
10731             
10732             if(this.labelxs > 0){
10733                 labelCfg.cls += ' col-xs-' + this.labelxs;
10734                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10735             }
10736             
10737             
10738         } else if ( this.fieldLabel.length) {
10739                 
10740             
10741             
10742             cfg.cn = [
10743                 {
10744                     tag : 'i',
10745                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10746                     tooltip : 'This field is required',
10747                     style : this.allowBlank ? ' display:none' : '' 
10748                 },
10749                 {
10750                     tag: 'label',
10751                    //cls : 'input-group-addon',
10752                     html : this.fieldLabel
10753
10754                 },
10755
10756                inputblock
10757
10758            ];
10759            
10760            if(this.indicatorpos == 'right'){
10761        
10762                 cfg.cn = [
10763                     {
10764                         tag: 'label',
10765                        //cls : 'input-group-addon',
10766                         html : this.fieldLabel
10767
10768                     },
10769                     {
10770                         tag : 'i',
10771                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10772                         tooltip : 'This field is required',
10773                         style : this.allowBlank ? ' display:none' : '' 
10774                     },
10775
10776                    inputblock
10777
10778                ];
10779
10780             }
10781
10782         } else {
10783             
10784             cfg.cn = [
10785
10786                     inputblock
10787
10788             ];
10789                 
10790                 
10791         };
10792         
10793         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10794            cfg.cls += ' navbar-form';
10795         }
10796         
10797         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10798             // on BS4 we do this only if not form 
10799             cfg.cls += ' navbar-form';
10800             cfg.tag = 'li';
10801         }
10802         
10803         return cfg;
10804         
10805     },
10806     /**
10807      * return the real input element.
10808      */
10809     inputEl: function ()
10810     {
10811         return this.el.select('input.form-control',true).first();
10812     },
10813     
10814     tooltipEl : function()
10815     {
10816         return this.inputEl();
10817     },
10818     
10819     indicatorEl : function()
10820     {
10821         if (Roo.bootstrap.version == 4) {
10822             return false; // not enabled in v4 yet.
10823         }
10824         
10825         var indicator = this.el.select('i.roo-required-indicator',true).first();
10826         
10827         if(!indicator){
10828             return false;
10829         }
10830         
10831         return indicator;
10832         
10833     },
10834     
10835     setDisabled : function(v)
10836     {
10837         var i  = this.inputEl().dom;
10838         if (!v) {
10839             i.removeAttribute('disabled');
10840             return;
10841             
10842         }
10843         i.setAttribute('disabled','true');
10844     },
10845     initEvents : function()
10846     {
10847           
10848         this.inputEl().on("keydown" , this.fireKey,  this);
10849         this.inputEl().on("focus", this.onFocus,  this);
10850         this.inputEl().on("blur", this.onBlur,  this);
10851         
10852         this.inputEl().relayEvent('keyup', this);
10853         
10854         this.indicator = this.indicatorEl();
10855         
10856         if(this.indicator){
10857             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10858         }
10859  
10860         // reference to original value for reset
10861         this.originalValue = this.getValue();
10862         //Roo.form.TextField.superclass.initEvents.call(this);
10863         if(this.validationEvent == 'keyup'){
10864             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10865             this.inputEl().on('keyup', this.filterValidation, this);
10866         }
10867         else if(this.validationEvent !== false){
10868             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10869         }
10870         
10871         if(this.selectOnFocus){
10872             this.on("focus", this.preFocus, this);
10873             
10874         }
10875         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10876             this.inputEl().on("keypress", this.filterKeys, this);
10877         } else {
10878             this.inputEl().relayEvent('keypress', this);
10879         }
10880        /* if(this.grow){
10881             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10882             this.el.on("click", this.autoSize,  this);
10883         }
10884         */
10885         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10886             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10887         }
10888         
10889         if (typeof(this.before) == 'object') {
10890             this.before.render(this.el.select('.roo-input-before',true).first());
10891         }
10892         if (typeof(this.after) == 'object') {
10893             this.after.render(this.el.select('.roo-input-after',true).first());
10894         }
10895         
10896         this.inputEl().on('change', this.onChange, this);
10897         
10898     },
10899     filterValidation : function(e){
10900         if(!e.isNavKeyPress()){
10901             this.validationTask.delay(this.validationDelay);
10902         }
10903     },
10904      /**
10905      * Validates the field value
10906      * @return {Boolean} True if the value is valid, else false
10907      */
10908     validate : function(){
10909         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10910         if(this.disabled || this.validateValue(this.getRawValue())){
10911             this.markValid();
10912             return true;
10913         }
10914         
10915         this.markInvalid();
10916         return false;
10917     },
10918     
10919     
10920     /**
10921      * Validates a value according to the field's validation rules and marks the field as invalid
10922      * if the validation fails
10923      * @param {Mixed} value The value to validate
10924      * @return {Boolean} True if the value is valid, else false
10925      */
10926     validateValue : function(value)
10927     {
10928         if(this.getVisibilityEl().hasClass('hidden')){
10929             return true;
10930         }
10931         
10932         if(value.length < 1)  { // if it's blank
10933             if(this.allowBlank){
10934                 return true;
10935             }
10936             return false;
10937         }
10938         
10939         if(value.length < this.minLength){
10940             return false;
10941         }
10942         if(value.length > this.maxLength){
10943             return false;
10944         }
10945         if(this.vtype){
10946             var vt = Roo.form.VTypes;
10947             if(!vt[this.vtype](value, this)){
10948                 return false;
10949             }
10950         }
10951         if(typeof this.validator == "function"){
10952             var msg = this.validator(value);
10953             if(msg !== true){
10954                 return false;
10955             }
10956             if (typeof(msg) == 'string') {
10957                 this.invalidText = msg;
10958             }
10959         }
10960         
10961         if(this.regex && !this.regex.test(value)){
10962             return false;
10963         }
10964         
10965         return true;
10966     },
10967     
10968      // private
10969     fireKey : function(e){
10970         //Roo.log('field ' + e.getKey());
10971         if(e.isNavKeyPress()){
10972             this.fireEvent("specialkey", this, e);
10973         }
10974     },
10975     focus : function (selectText){
10976         if(this.rendered){
10977             this.inputEl().focus();
10978             if(selectText === true){
10979                 this.inputEl().dom.select();
10980             }
10981         }
10982         return this;
10983     } ,
10984     
10985     onFocus : function(){
10986         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10987            // this.el.addClass(this.focusClass);
10988         }
10989         if(!this.hasFocus){
10990             this.hasFocus = true;
10991             this.startValue = this.getValue();
10992             this.fireEvent("focus", this);
10993         }
10994     },
10995     
10996     beforeBlur : Roo.emptyFn,
10997
10998     
10999     // private
11000     onBlur : function(){
11001         this.beforeBlur();
11002         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11003             //this.el.removeClass(this.focusClass);
11004         }
11005         this.hasFocus = false;
11006         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11007             this.validate();
11008         }
11009         var v = this.getValue();
11010         if(String(v) !== String(this.startValue)){
11011             this.fireEvent('change', this, v, this.startValue);
11012         }
11013         this.fireEvent("blur", this);
11014     },
11015     
11016     onChange : function(e)
11017     {
11018         var v = this.getValue();
11019         if(String(v) !== String(this.startValue)){
11020             this.fireEvent('change', this, v, this.startValue);
11021         }
11022         
11023     },
11024     
11025     /**
11026      * Resets the current field value to the originally loaded value and clears any validation messages
11027      */
11028     reset : function(){
11029         this.setValue(this.originalValue);
11030         this.validate();
11031     },
11032      /**
11033      * Returns the name of the field
11034      * @return {Mixed} name The name field
11035      */
11036     getName: function(){
11037         return this.name;
11038     },
11039      /**
11040      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11041      * @return {Mixed} value The field value
11042      */
11043     getValue : function(){
11044         
11045         var v = this.inputEl().getValue();
11046         
11047         return v;
11048     },
11049     /**
11050      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11051      * @return {Mixed} value The field value
11052      */
11053     getRawValue : function(){
11054         var v = this.inputEl().getValue();
11055         
11056         return v;
11057     },
11058     
11059     /**
11060      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11061      * @param {Mixed} value The value to set
11062      */
11063     setRawValue : function(v){
11064         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11065     },
11066     
11067     selectText : function(start, end){
11068         var v = this.getRawValue();
11069         if(v.length > 0){
11070             start = start === undefined ? 0 : start;
11071             end = end === undefined ? v.length : end;
11072             var d = this.inputEl().dom;
11073             if(d.setSelectionRange){
11074                 d.setSelectionRange(start, end);
11075             }else if(d.createTextRange){
11076                 var range = d.createTextRange();
11077                 range.moveStart("character", start);
11078                 range.moveEnd("character", v.length-end);
11079                 range.select();
11080             }
11081         }
11082     },
11083     
11084     /**
11085      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11086      * @param {Mixed} value The value to set
11087      */
11088     setValue : function(v){
11089         this.value = v;
11090         if(this.rendered){
11091             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11092             this.validate();
11093         }
11094     },
11095     
11096     /*
11097     processValue : function(value){
11098         if(this.stripCharsRe){
11099             var newValue = value.replace(this.stripCharsRe, '');
11100             if(newValue !== value){
11101                 this.setRawValue(newValue);
11102                 return newValue;
11103             }
11104         }
11105         return value;
11106     },
11107   */
11108     preFocus : function(){
11109         
11110         if(this.selectOnFocus){
11111             this.inputEl().dom.select();
11112         }
11113     },
11114     filterKeys : function(e){
11115         var k = e.getKey();
11116         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11117             return;
11118         }
11119         var c = e.getCharCode(), cc = String.fromCharCode(c);
11120         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11121             return;
11122         }
11123         if(!this.maskRe.test(cc)){
11124             e.stopEvent();
11125         }
11126     },
11127      /**
11128      * Clear any invalid styles/messages for this field
11129      */
11130     clearInvalid : function(){
11131         
11132         if(!this.el || this.preventMark){ // not rendered
11133             return;
11134         }
11135         
11136         
11137         this.el.removeClass([this.invalidClass, 'is-invalid']);
11138         
11139         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11140             
11141             var feedback = this.el.select('.form-control-feedback', true).first();
11142             
11143             if(feedback){
11144                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11145             }
11146             
11147         }
11148         
11149         if(this.indicator){
11150             this.indicator.removeClass('visible');
11151             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11152         }
11153         
11154         this.fireEvent('valid', this);
11155     },
11156     
11157      /**
11158      * Mark this field as valid
11159      */
11160     markValid : function()
11161     {
11162         if(!this.el  || this.preventMark){ // not rendered...
11163             return;
11164         }
11165         
11166         this.el.removeClass([this.invalidClass, this.validClass]);
11167         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11168
11169         var feedback = this.el.select('.form-control-feedback', true).first();
11170             
11171         if(feedback){
11172             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11173         }
11174         
11175         if(this.indicator){
11176             this.indicator.removeClass('visible');
11177             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11178         }
11179         
11180         if(this.disabled){
11181             return;
11182         }
11183         
11184            
11185         if(this.allowBlank && !this.getRawValue().length){
11186             return;
11187         }
11188         if (Roo.bootstrap.version == 3) {
11189             this.el.addClass(this.validClass);
11190         } else {
11191             this.inputEl().addClass('is-valid');
11192         }
11193
11194         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11195             
11196             var feedback = this.el.select('.form-control-feedback', true).first();
11197             
11198             if(feedback){
11199                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11200                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11201             }
11202             
11203         }
11204         
11205         this.fireEvent('valid', this);
11206     },
11207     
11208      /**
11209      * Mark this field as invalid
11210      * @param {String} msg The validation message
11211      */
11212     markInvalid : function(msg)
11213     {
11214         if(!this.el  || this.preventMark){ // not rendered
11215             return;
11216         }
11217         
11218         this.el.removeClass([this.invalidClass, this.validClass]);
11219         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11220         
11221         var feedback = this.el.select('.form-control-feedback', true).first();
11222             
11223         if(feedback){
11224             this.el.select('.form-control-feedback', true).first().removeClass(
11225                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11226         }
11227
11228         if(this.disabled){
11229             return;
11230         }
11231         
11232         if(this.allowBlank && !this.getRawValue().length){
11233             return;
11234         }
11235         
11236         if(this.indicator){
11237             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11238             this.indicator.addClass('visible');
11239         }
11240         if (Roo.bootstrap.version == 3) {
11241             this.el.addClass(this.invalidClass);
11242         } else {
11243             this.inputEl().addClass('is-invalid');
11244         }
11245         
11246         
11247         
11248         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11249             
11250             var feedback = this.el.select('.form-control-feedback', true).first();
11251             
11252             if(feedback){
11253                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11254                 
11255                 if(this.getValue().length || this.forceFeedback){
11256                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11257                 }
11258                 
11259             }
11260             
11261         }
11262         
11263         this.fireEvent('invalid', this, msg);
11264     },
11265     // private
11266     SafariOnKeyDown : function(event)
11267     {
11268         // this is a workaround for a password hang bug on chrome/ webkit.
11269         if (this.inputEl().dom.type != 'password') {
11270             return;
11271         }
11272         
11273         var isSelectAll = false;
11274         
11275         if(this.inputEl().dom.selectionEnd > 0){
11276             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11277         }
11278         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11279             event.preventDefault();
11280             this.setValue('');
11281             return;
11282         }
11283         
11284         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11285             
11286             event.preventDefault();
11287             // this is very hacky as keydown always get's upper case.
11288             //
11289             var cc = String.fromCharCode(event.getCharCode());
11290             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11291             
11292         }
11293     },
11294     adjustWidth : function(tag, w){
11295         tag = tag.toLowerCase();
11296         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11297             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11298                 if(tag == 'input'){
11299                     return w + 2;
11300                 }
11301                 if(tag == 'textarea'){
11302                     return w-2;
11303                 }
11304             }else if(Roo.isOpera){
11305                 if(tag == 'input'){
11306                     return w + 2;
11307                 }
11308                 if(tag == 'textarea'){
11309                     return w-2;
11310                 }
11311             }
11312         }
11313         return w;
11314     },
11315     
11316     setFieldLabel : function(v)
11317     {
11318         if(!this.rendered){
11319             return;
11320         }
11321         
11322         if(this.indicatorEl()){
11323             var ar = this.el.select('label > span',true);
11324             
11325             if (ar.elements.length) {
11326                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11327                 this.fieldLabel = v;
11328                 return;
11329             }
11330             
11331             var br = this.el.select('label',true);
11332             
11333             if(br.elements.length) {
11334                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11335                 this.fieldLabel = v;
11336                 return;
11337             }
11338             
11339             Roo.log('Cannot Found any of label > span || label in input');
11340             return;
11341         }
11342         
11343         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11344         this.fieldLabel = v;
11345         
11346         
11347     }
11348 });
11349
11350  
11351 /*
11352  * - LGPL
11353  *
11354  * Input
11355  * 
11356  */
11357
11358 /**
11359  * @class Roo.bootstrap.TextArea
11360  * @extends Roo.bootstrap.Input
11361  * Bootstrap TextArea class
11362  * @cfg {Number} cols Specifies the visible width of a text area
11363  * @cfg {Number} rows Specifies the visible number of lines in a text area
11364  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11365  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11366  * @cfg {string} html text
11367  * 
11368  * @constructor
11369  * Create a new TextArea
11370  * @param {Object} config The config object
11371  */
11372
11373 Roo.bootstrap.TextArea = function(config){
11374     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11375    
11376 };
11377
11378 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11379      
11380     cols : false,
11381     rows : 5,
11382     readOnly : false,
11383     warp : 'soft',
11384     resize : false,
11385     value: false,
11386     html: false,
11387     
11388     getAutoCreate : function(){
11389         
11390         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11391         
11392         var id = Roo.id();
11393         
11394         var cfg = {};
11395         
11396         if(this.inputType != 'hidden'){
11397             cfg.cls = 'form-group' //input-group
11398         }
11399         
11400         var input =  {
11401             tag: 'textarea',
11402             id : id,
11403             warp : this.warp,
11404             rows : this.rows,
11405             value : this.value || '',
11406             html: this.html || '',
11407             cls : 'form-control',
11408             placeholder : this.placeholder || '' 
11409             
11410         };
11411         
11412         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11413             input.maxLength = this.maxLength;
11414         }
11415         
11416         if(this.resize){
11417             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11418         }
11419         
11420         if(this.cols){
11421             input.cols = this.cols;
11422         }
11423         
11424         if (this.readOnly) {
11425             input.readonly = true;
11426         }
11427         
11428         if (this.name) {
11429             input.name = this.name;
11430         }
11431         
11432         if (this.size) {
11433             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11434         }
11435         
11436         var settings=this;
11437         ['xs','sm','md','lg'].map(function(size){
11438             if (settings[size]) {
11439                 cfg.cls += ' col-' + size + '-' + settings[size];
11440             }
11441         });
11442         
11443         var inputblock = input;
11444         
11445         if(this.hasFeedback && !this.allowBlank){
11446             
11447             var feedback = {
11448                 tag: 'span',
11449                 cls: 'glyphicon form-control-feedback'
11450             };
11451
11452             inputblock = {
11453                 cls : 'has-feedback',
11454                 cn :  [
11455                     input,
11456                     feedback
11457                 ] 
11458             };  
11459         }
11460         
11461         
11462         if (this.before || this.after) {
11463             
11464             inputblock = {
11465                 cls : 'input-group',
11466                 cn :  [] 
11467             };
11468             if (this.before) {
11469                 inputblock.cn.push({
11470                     tag :'span',
11471                     cls : 'input-group-addon',
11472                     html : this.before
11473                 });
11474             }
11475             
11476             inputblock.cn.push(input);
11477             
11478             if(this.hasFeedback && !this.allowBlank){
11479                 inputblock.cls += ' has-feedback';
11480                 inputblock.cn.push(feedback);
11481             }
11482             
11483             if (this.after) {
11484                 inputblock.cn.push({
11485                     tag :'span',
11486                     cls : 'input-group-addon',
11487                     html : this.after
11488                 });
11489             }
11490             
11491         }
11492         
11493         if (align ==='left' && this.fieldLabel.length) {
11494             cfg.cn = [
11495                 {
11496                     tag: 'label',
11497                     'for' :  id,
11498                     cls : 'control-label',
11499                     html : this.fieldLabel
11500                 },
11501                 {
11502                     cls : "",
11503                     cn: [
11504                         inputblock
11505                     ]
11506                 }
11507
11508             ];
11509             
11510             if(this.labelWidth > 12){
11511                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11512             }
11513
11514             if(this.labelWidth < 13 && this.labelmd == 0){
11515                 this.labelmd = this.labelWidth;
11516             }
11517
11518             if(this.labellg > 0){
11519                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11520                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11521             }
11522
11523             if(this.labelmd > 0){
11524                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11525                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11526             }
11527
11528             if(this.labelsm > 0){
11529                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11530                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11531             }
11532
11533             if(this.labelxs > 0){
11534                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11535                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11536             }
11537             
11538         } else if ( this.fieldLabel.length) {
11539             cfg.cn = [
11540
11541                {
11542                    tag: 'label',
11543                    //cls : 'input-group-addon',
11544                    html : this.fieldLabel
11545
11546                },
11547
11548                inputblock
11549
11550            ];
11551
11552         } else {
11553
11554             cfg.cn = [
11555
11556                 inputblock
11557
11558             ];
11559                 
11560         }
11561         
11562         if (this.disabled) {
11563             input.disabled=true;
11564         }
11565         
11566         return cfg;
11567         
11568     },
11569     /**
11570      * return the real textarea element.
11571      */
11572     inputEl: function ()
11573     {
11574         return this.el.select('textarea.form-control',true).first();
11575     },
11576     
11577     /**
11578      * Clear any invalid styles/messages for this field
11579      */
11580     clearInvalid : function()
11581     {
11582         
11583         if(!this.el || this.preventMark){ // not rendered
11584             return;
11585         }
11586         
11587         var label = this.el.select('label', true).first();
11588         var icon = this.el.select('i.fa-star', true).first();
11589         
11590         if(label && icon){
11591             icon.remove();
11592         }
11593         this.el.removeClass( this.validClass);
11594         this.inputEl().removeClass('is-invalid');
11595          
11596         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11597             
11598             var feedback = this.el.select('.form-control-feedback', true).first();
11599             
11600             if(feedback){
11601                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11602             }
11603             
11604         }
11605         
11606         this.fireEvent('valid', this);
11607     },
11608     
11609      /**
11610      * Mark this field as valid
11611      */
11612     markValid : function()
11613     {
11614         if(!this.el  || this.preventMark){ // not rendered
11615             return;
11616         }
11617         
11618         this.el.removeClass([this.invalidClass, this.validClass]);
11619         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11620         
11621         var feedback = this.el.select('.form-control-feedback', true).first();
11622             
11623         if(feedback){
11624             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11625         }
11626
11627         if(this.disabled || this.allowBlank){
11628             return;
11629         }
11630         
11631         var label = this.el.select('label', true).first();
11632         var icon = this.el.select('i.fa-star', true).first();
11633         
11634         if(label && icon){
11635             icon.remove();
11636         }
11637         if (Roo.bootstrap.version == 3) {
11638             this.el.addClass(this.validClass);
11639         } else {
11640             this.inputEl().addClass('is-valid');
11641         }
11642         
11643         
11644         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11645             
11646             var feedback = this.el.select('.form-control-feedback', true).first();
11647             
11648             if(feedback){
11649                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11650                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11651             }
11652             
11653         }
11654         
11655         this.fireEvent('valid', this);
11656     },
11657     
11658      /**
11659      * Mark this field as invalid
11660      * @param {String} msg The validation message
11661      */
11662     markInvalid : function(msg)
11663     {
11664         if(!this.el  || this.preventMark){ // not rendered
11665             return;
11666         }
11667         
11668         this.el.removeClass([this.invalidClass, this.validClass]);
11669         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11670         
11671         var feedback = this.el.select('.form-control-feedback', true).first();
11672             
11673         if(feedback){
11674             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11675         }
11676
11677         if(this.disabled || this.allowBlank){
11678             return;
11679         }
11680         
11681         var label = this.el.select('label', true).first();
11682         var icon = this.el.select('i.fa-star', true).first();
11683         
11684         if(!this.getValue().length && label && !icon){
11685             this.el.createChild({
11686                 tag : 'i',
11687                 cls : 'text-danger fa fa-lg fa-star',
11688                 tooltip : 'This field is required',
11689                 style : 'margin-right:5px;'
11690             }, label, true);
11691         }
11692         
11693         if (Roo.bootstrap.version == 3) {
11694             this.el.addClass(this.invalidClass);
11695         } else {
11696             this.inputEl().addClass('is-invalid');
11697         }
11698         
11699         // fixme ... this may be depricated need to test..
11700         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11701             
11702             var feedback = this.el.select('.form-control-feedback', true).first();
11703             
11704             if(feedback){
11705                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11706                 
11707                 if(this.getValue().length || this.forceFeedback){
11708                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11709                 }
11710                 
11711             }
11712             
11713         }
11714         
11715         this.fireEvent('invalid', this, msg);
11716     }
11717 });
11718
11719  
11720 /*
11721  * - LGPL
11722  *
11723  * trigger field - base class for combo..
11724  * 
11725  */
11726  
11727 /**
11728  * @class Roo.bootstrap.TriggerField
11729  * @extends Roo.bootstrap.Input
11730  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11731  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11732  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11733  * for which you can provide a custom implementation.  For example:
11734  * <pre><code>
11735 var trigger = new Roo.bootstrap.TriggerField();
11736 trigger.onTriggerClick = myTriggerFn;
11737 trigger.applyTo('my-field');
11738 </code></pre>
11739  *
11740  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11741  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11742  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11743  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11744  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11745
11746  * @constructor
11747  * Create a new TriggerField.
11748  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11749  * to the base TextField)
11750  */
11751 Roo.bootstrap.TriggerField = function(config){
11752     this.mimicing = false;
11753     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11754 };
11755
11756 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11757     /**
11758      * @cfg {String} triggerClass A CSS class to apply to the trigger
11759      */
11760      /**
11761      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11762      */
11763     hideTrigger:false,
11764
11765     /**
11766      * @cfg {Boolean} removable (true|false) special filter default false
11767      */
11768     removable : false,
11769     
11770     /** @cfg {Boolean} grow @hide */
11771     /** @cfg {Number} growMin @hide */
11772     /** @cfg {Number} growMax @hide */
11773
11774     /**
11775      * @hide 
11776      * @method
11777      */
11778     autoSize: Roo.emptyFn,
11779     // private
11780     monitorTab : true,
11781     // private
11782     deferHeight : true,
11783
11784     
11785     actionMode : 'wrap',
11786     
11787     caret : false,
11788     
11789     
11790     getAutoCreate : function(){
11791        
11792         var align = this.labelAlign || this.parentLabelAlign();
11793         
11794         var id = Roo.id();
11795         
11796         var cfg = {
11797             cls: 'form-group' //input-group
11798         };
11799         
11800         
11801         var input =  {
11802             tag: 'input',
11803             id : id,
11804             type : this.inputType,
11805             cls : 'form-control',
11806             autocomplete: 'new-password',
11807             placeholder : this.placeholder || '' 
11808             
11809         };
11810         if (this.name) {
11811             input.name = this.name;
11812         }
11813         if (this.size) {
11814             input.cls += ' input-' + this.size;
11815         }
11816         
11817         if (this.disabled) {
11818             input.disabled=true;
11819         }
11820         
11821         var inputblock = input;
11822         
11823         if(this.hasFeedback && !this.allowBlank){
11824             
11825             var feedback = {
11826                 tag: 'span',
11827                 cls: 'glyphicon form-control-feedback'
11828             };
11829             
11830             if(this.removable && !this.editable  ){
11831                 inputblock = {
11832                     cls : 'has-feedback',
11833                     cn :  [
11834                         inputblock,
11835                         {
11836                             tag: 'button',
11837                             html : 'x',
11838                             cls : 'roo-combo-removable-btn close'
11839                         },
11840                         feedback
11841                     ] 
11842                 };
11843             } else {
11844                 inputblock = {
11845                     cls : 'has-feedback',
11846                     cn :  [
11847                         inputblock,
11848                         feedback
11849                     ] 
11850                 };
11851             }
11852
11853         } else {
11854             if(this.removable && !this.editable ){
11855                 inputblock = {
11856                     cls : 'roo-removable',
11857                     cn :  [
11858                         inputblock,
11859                         {
11860                             tag: 'button',
11861                             html : 'x',
11862                             cls : 'roo-combo-removable-btn close'
11863                         }
11864                     ] 
11865                 };
11866             }
11867         }
11868         
11869         if (this.before || this.after) {
11870             
11871             inputblock = {
11872                 cls : 'input-group',
11873                 cn :  [] 
11874             };
11875             if (this.before) {
11876                 inputblock.cn.push({
11877                     tag :'span',
11878                     cls : 'input-group-addon input-group-prepend input-group-text',
11879                     html : this.before
11880                 });
11881             }
11882             
11883             inputblock.cn.push(input);
11884             
11885             if(this.hasFeedback && !this.allowBlank){
11886                 inputblock.cls += ' has-feedback';
11887                 inputblock.cn.push(feedback);
11888             }
11889             
11890             if (this.after) {
11891                 inputblock.cn.push({
11892                     tag :'span',
11893                     cls : 'input-group-addon input-group-append input-group-text',
11894                     html : this.after
11895                 });
11896             }
11897             
11898         };
11899         
11900       
11901         
11902         var ibwrap = inputblock;
11903         
11904         if(this.multiple){
11905             ibwrap = {
11906                 tag: 'ul',
11907                 cls: 'roo-select2-choices',
11908                 cn:[
11909                     {
11910                         tag: 'li',
11911                         cls: 'roo-select2-search-field',
11912                         cn: [
11913
11914                             inputblock
11915                         ]
11916                     }
11917                 ]
11918             };
11919                 
11920         }
11921         
11922         var combobox = {
11923             cls: 'roo-select2-container input-group',
11924             cn: [
11925                  {
11926                     tag: 'input',
11927                     type : 'hidden',
11928                     cls: 'form-hidden-field'
11929                 },
11930                 ibwrap
11931             ]
11932         };
11933         
11934         if(!this.multiple && this.showToggleBtn){
11935             
11936             var caret = {
11937                         tag: 'span',
11938                         cls: 'caret'
11939              };
11940             if (this.caret != false) {
11941                 caret = {
11942                      tag: 'i',
11943                      cls: 'fa fa-' + this.caret
11944                 };
11945                 
11946             }
11947             
11948             combobox.cn.push({
11949                 tag :'span',
11950                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11951                 cn : [
11952                     Roo.bootstrap.version == 3 ? caret : '',
11953                     {
11954                         tag: 'span',
11955                         cls: 'combobox-clear',
11956                         cn  : [
11957                             {
11958                                 tag : 'i',
11959                                 cls: 'icon-remove'
11960                             }
11961                         ]
11962                     }
11963                 ]
11964
11965             })
11966         }
11967         
11968         if(this.multiple){
11969             combobox.cls += ' roo-select2-container-multi';
11970         }
11971          var indicator = {
11972             tag : 'i',
11973             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11974             tooltip : 'This field is required'
11975         };
11976         if (Roo.bootstrap.version == 4) {
11977             indicator = {
11978                 tag : 'i',
11979                 style : 'display:none'
11980             };
11981         }
11982         
11983         
11984         if (align ==='left' && this.fieldLabel.length) {
11985             
11986             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11987
11988             cfg.cn = [
11989                 indicator,
11990                 {
11991                     tag: 'label',
11992                     'for' :  id,
11993                     cls : 'control-label',
11994                     html : this.fieldLabel
11995
11996                 },
11997                 {
11998                     cls : "", 
11999                     cn: [
12000                         combobox
12001                     ]
12002                 }
12003
12004             ];
12005             
12006             var labelCfg = cfg.cn[1];
12007             var contentCfg = cfg.cn[2];
12008             
12009             if(this.indicatorpos == 'right'){
12010                 cfg.cn = [
12011                     {
12012                         tag: 'label',
12013                         'for' :  id,
12014                         cls : 'control-label',
12015                         cn : [
12016                             {
12017                                 tag : 'span',
12018                                 html : this.fieldLabel
12019                             },
12020                             indicator
12021                         ]
12022                     },
12023                     {
12024                         cls : "", 
12025                         cn: [
12026                             combobox
12027                         ]
12028                     }
12029
12030                 ];
12031                 
12032                 labelCfg = cfg.cn[0];
12033                 contentCfg = cfg.cn[1];
12034             }
12035             
12036             if(this.labelWidth > 12){
12037                 labelCfg.style = "width: " + this.labelWidth + 'px';
12038             }
12039             
12040             if(this.labelWidth < 13 && this.labelmd == 0){
12041                 this.labelmd = this.labelWidth;
12042             }
12043             
12044             if(this.labellg > 0){
12045                 labelCfg.cls += ' col-lg-' + this.labellg;
12046                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12047             }
12048             
12049             if(this.labelmd > 0){
12050                 labelCfg.cls += ' col-md-' + this.labelmd;
12051                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12052             }
12053             
12054             if(this.labelsm > 0){
12055                 labelCfg.cls += ' col-sm-' + this.labelsm;
12056                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12057             }
12058             
12059             if(this.labelxs > 0){
12060                 labelCfg.cls += ' col-xs-' + this.labelxs;
12061                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12062             }
12063             
12064         } else if ( this.fieldLabel.length) {
12065 //                Roo.log(" label");
12066             cfg.cn = [
12067                 indicator,
12068                {
12069                    tag: 'label',
12070                    //cls : 'input-group-addon',
12071                    html : this.fieldLabel
12072
12073                },
12074
12075                combobox
12076
12077             ];
12078             
12079             if(this.indicatorpos == 'right'){
12080                 
12081                 cfg.cn = [
12082                     {
12083                        tag: 'label',
12084                        cn : [
12085                            {
12086                                tag : 'span',
12087                                html : this.fieldLabel
12088                            },
12089                            indicator
12090                        ]
12091
12092                     },
12093                     combobox
12094
12095                 ];
12096
12097             }
12098
12099         } else {
12100             
12101 //                Roo.log(" no label && no align");
12102                 cfg = combobox
12103                      
12104                 
12105         }
12106         
12107         var settings=this;
12108         ['xs','sm','md','lg'].map(function(size){
12109             if (settings[size]) {
12110                 cfg.cls += ' col-' + size + '-' + settings[size];
12111             }
12112         });
12113         
12114         return cfg;
12115         
12116     },
12117     
12118     
12119     
12120     // private
12121     onResize : function(w, h){
12122 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12123 //        if(typeof w == 'number'){
12124 //            var x = w - this.trigger.getWidth();
12125 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12126 //            this.trigger.setStyle('left', x+'px');
12127 //        }
12128     },
12129
12130     // private
12131     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12132
12133     // private
12134     getResizeEl : function(){
12135         return this.inputEl();
12136     },
12137
12138     // private
12139     getPositionEl : function(){
12140         return this.inputEl();
12141     },
12142
12143     // private
12144     alignErrorIcon : function(){
12145         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12146     },
12147
12148     // private
12149     initEvents : function(){
12150         
12151         this.createList();
12152         
12153         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12154         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12155         if(!this.multiple && this.showToggleBtn){
12156             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12157             if(this.hideTrigger){
12158                 this.trigger.setDisplayed(false);
12159             }
12160             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12161         }
12162         
12163         if(this.multiple){
12164             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12165         }
12166         
12167         if(this.removable && !this.editable && !this.tickable){
12168             var close = this.closeTriggerEl();
12169             
12170             if(close){
12171                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12172                 close.on('click', this.removeBtnClick, this, close);
12173             }
12174         }
12175         
12176         //this.trigger.addClassOnOver('x-form-trigger-over');
12177         //this.trigger.addClassOnClick('x-form-trigger-click');
12178         
12179         //if(!this.width){
12180         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12181         //}
12182     },
12183     
12184     closeTriggerEl : function()
12185     {
12186         var close = this.el.select('.roo-combo-removable-btn', true).first();
12187         return close ? close : false;
12188     },
12189     
12190     removeBtnClick : function(e, h, el)
12191     {
12192         e.preventDefault();
12193         
12194         if(this.fireEvent("remove", this) !== false){
12195             this.reset();
12196             this.fireEvent("afterremove", this)
12197         }
12198     },
12199     
12200     createList : function()
12201     {
12202         this.list = Roo.get(document.body).createChild({
12203             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12204             cls: 'typeahead typeahead-long dropdown-menu',
12205             style: 'display:none'
12206         });
12207         
12208         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12209         
12210     },
12211
12212     // private
12213     initTrigger : function(){
12214        
12215     },
12216
12217     // private
12218     onDestroy : function(){
12219         if(this.trigger){
12220             this.trigger.removeAllListeners();
12221           //  this.trigger.remove();
12222         }
12223         //if(this.wrap){
12224         //    this.wrap.remove();
12225         //}
12226         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12227     },
12228
12229     // private
12230     onFocus : function(){
12231         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12232         /*
12233         if(!this.mimicing){
12234             this.wrap.addClass('x-trigger-wrap-focus');
12235             this.mimicing = true;
12236             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12237             if(this.monitorTab){
12238                 this.el.on("keydown", this.checkTab, this);
12239             }
12240         }
12241         */
12242     },
12243
12244     // private
12245     checkTab : function(e){
12246         if(e.getKey() == e.TAB){
12247             this.triggerBlur();
12248         }
12249     },
12250
12251     // private
12252     onBlur : function(){
12253         // do nothing
12254     },
12255
12256     // private
12257     mimicBlur : function(e, t){
12258         /*
12259         if(!this.wrap.contains(t) && this.validateBlur()){
12260             this.triggerBlur();
12261         }
12262         */
12263     },
12264
12265     // private
12266     triggerBlur : function(){
12267         this.mimicing = false;
12268         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12269         if(this.monitorTab){
12270             this.el.un("keydown", this.checkTab, this);
12271         }
12272         //this.wrap.removeClass('x-trigger-wrap-focus');
12273         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12274     },
12275
12276     // private
12277     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12278     validateBlur : function(e, t){
12279         return true;
12280     },
12281
12282     // private
12283     onDisable : function(){
12284         this.inputEl().dom.disabled = true;
12285         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12286         //if(this.wrap){
12287         //    this.wrap.addClass('x-item-disabled');
12288         //}
12289     },
12290
12291     // private
12292     onEnable : function(){
12293         this.inputEl().dom.disabled = false;
12294         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12295         //if(this.wrap){
12296         //    this.el.removeClass('x-item-disabled');
12297         //}
12298     },
12299
12300     // private
12301     onShow : function(){
12302         var ae = this.getActionEl();
12303         
12304         if(ae){
12305             ae.dom.style.display = '';
12306             ae.dom.style.visibility = 'visible';
12307         }
12308     },
12309
12310     // private
12311     
12312     onHide : function(){
12313         var ae = this.getActionEl();
12314         ae.dom.style.display = 'none';
12315     },
12316
12317     /**
12318      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12319      * by an implementing function.
12320      * @method
12321      * @param {EventObject} e
12322      */
12323     onTriggerClick : Roo.emptyFn
12324 });
12325  
12326 /*
12327 * Licence: LGPL
12328 */
12329
12330 /**
12331  * @class Roo.bootstrap.CardUploader
12332  * @extends Roo.bootstrap.Button
12333  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12334  * @cfg {Number} errorTimeout default 3000
12335  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12336  * @cfg {Array}  html The button text.
12337
12338  *
12339  * @constructor
12340  * Create a new CardUploader
12341  * @param {Object} config The config object
12342  */
12343
12344 Roo.bootstrap.CardUploader = function(config){
12345     
12346  
12347     
12348     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12349     
12350     
12351     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12352         return r.data.id
12353         });
12354     
12355     
12356 };
12357
12358 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12359     
12360      
12361     errorTimeout : 3000,
12362      
12363     images : false,
12364    
12365     fileCollection : false,
12366     allowBlank : true,
12367     
12368     getAutoCreate : function()
12369     {
12370         
12371         var cfg =  {
12372             cls :'form-group' ,
12373             cn : [
12374                
12375                 {
12376                     tag: 'label',
12377                    //cls : 'input-group-addon',
12378                     html : this.fieldLabel
12379
12380                 },
12381
12382                 {
12383                     tag: 'input',
12384                     type : 'hidden',
12385                     value : this.value,
12386                     cls : 'd-none  form-control'
12387                 },
12388                 
12389                 {
12390                     tag: 'input',
12391                     multiple : 'multiple',
12392                     type : 'file',
12393                     cls : 'd-none  roo-card-upload-selector'
12394                 },
12395                 
12396                 {
12397                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12398                 },
12399                 {
12400                     cls : 'card-columns roo-card-uploader-container'
12401                 }
12402
12403             ]
12404         };
12405            
12406          
12407         return cfg;
12408     },
12409     
12410     getChildContainer : function() /// what children are added to.
12411     {
12412         return this.containerEl;
12413     },
12414    
12415     getButtonContainer : function() /// what children are added to.
12416     {
12417         return this.el.select(".roo-card-uploader-button-container").first();
12418     },
12419    
12420     initEvents : function()
12421     {
12422         
12423         Roo.bootstrap.Input.prototype.initEvents.call(this);
12424         
12425         var t = this;
12426         this.addxtype({
12427             xns: Roo.bootstrap,
12428
12429             xtype : 'Button',
12430             container_method : 'getButtonContainer' ,            
12431             html :  this.html, // fix changable?
12432             cls : 'w-100 ',
12433             listeners : {
12434                 'click' : function(btn, e) {
12435                     t.onClick(e);
12436                 }
12437             }
12438         });
12439         
12440         
12441         
12442         
12443         this.urlAPI = (window.createObjectURL && window) || 
12444                                 (window.URL && URL.revokeObjectURL && URL) || 
12445                                 (window.webkitURL && webkitURL);
12446                         
12447          
12448          
12449          
12450         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12451         
12452         this.selectorEl.on('change', this.onFileSelected, this);
12453         if (this.images) {
12454             var t = this;
12455             this.images.forEach(function(img) {
12456                 t.addCard(img)
12457             });
12458             this.images = false;
12459         }
12460         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12461          
12462        
12463     },
12464     
12465    
12466     onClick : function(e)
12467     {
12468         e.preventDefault();
12469          
12470         this.selectorEl.dom.click();
12471          
12472     },
12473     
12474     onFileSelected : function(e)
12475     {
12476         e.preventDefault();
12477         
12478         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12479             return;
12480         }
12481         
12482         Roo.each(this.selectorEl.dom.files, function(file){    
12483             this.addFile(file);
12484         }, this);
12485          
12486     },
12487     
12488       
12489     
12490       
12491     
12492     addFile : function(file)
12493     {
12494            
12495         if(typeof(file) === 'string'){
12496             throw "Add file by name?"; // should not happen
12497             return;
12498         }
12499         
12500         if(!file || !this.urlAPI){
12501             return;
12502         }
12503         
12504         // file;
12505         // file.type;
12506         
12507         var _this = this;
12508         
12509         
12510         var url = _this.urlAPI.createObjectURL( file);
12511            
12512         this.addCard({
12513             id : Roo.bootstrap.CardUploader.ID--,
12514             is_uploaded : false,
12515             src : url,
12516             title : file.name,
12517             mimetype : file.type,
12518             preview : false,
12519             is_deleted : 0
12520         })
12521         
12522     },
12523     
12524     addCard : function (data)
12525     {
12526         // hidden input element?
12527         // if the file is not an image...
12528         //then we need to use something other that and header_image
12529         var t = this;
12530         //   remove.....
12531         var footer = [
12532             {
12533                 xns : Roo.bootstrap,
12534                 xtype : 'CardFooter',
12535                 items: [
12536                     {
12537                         xns : Roo.bootstrap,
12538                         xtype : 'Element',
12539                         cls : 'd-flex',
12540                         items : [
12541                             
12542                             {
12543                                 xns : Roo.bootstrap,
12544                                 xtype : 'Button',
12545                                 html : String.format("<small>{0}</small>", data.title),
12546                                 cls : 'col-11 text-left',
12547                                 size: 'sm',
12548                                 weight: 'link',
12549                                 fa : 'download',
12550                                 listeners : {
12551                                     click : function() {
12552                                         this.downloadCard(data.id)
12553                                     }
12554                                 }
12555                             },
12556                           
12557                             {
12558                                 xns : Roo.bootstrap,
12559                                 xtype : 'Button',
12560                                 
12561                                 size : 'sm',
12562                                 weight: 'danger',
12563                                 cls : 'col-1',
12564                                 fa : 'times',
12565                                 listeners : {
12566                                     click : function() {
12567                                         t.removeCard(data.id)
12568                                     }
12569                                 }
12570                             }
12571                         ]
12572                     }
12573                     
12574                 ] 
12575             }
12576             
12577         ];
12578
12579         var cn = this.addxtype(
12580             {
12581                  
12582                 xns : Roo.bootstrap,
12583                 xtype : 'Card',
12584                 closeable : true,
12585                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12586                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12587                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12588                 data : data,
12589                 html : false,
12590                  
12591                 items : footer,
12592                 initEvents : function() {
12593                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12594                     this.imgEl = this.el.select('.card-img-top').first();
12595                     if (this.imgEl) {
12596                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12597                         this.imgEl.set({ 'pointer' : 'cursor' });
12598                                   
12599                     }
12600                     
12601                   
12602                 }
12603                 
12604             }
12605         );
12606         // dont' really need ot update items.
12607         // this.items.push(cn);
12608         this.fileCollection.add(cn);
12609         this.updateInput();
12610         
12611     },
12612     removeCard : function(id)
12613     {
12614         
12615         var card  = this.fileCollection.get(id);
12616         card.data.is_deleted = 1;
12617         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12618         this.fileCollection.remove(card);
12619         //this.items = this.items.filter(function(e) { return e != card });
12620         // dont' really need ot update items.
12621         card.el.dom.parentNode.removeChild(card.el.dom);
12622         
12623     },
12624     reset: function()
12625     {
12626         this.fileCollection.each(function(card) {
12627             card.el.dom.parentNode.removeChild(card.el.dom);    
12628         });
12629         this.fileCollection.clear();
12630         this.updateInput();
12631     },
12632     
12633     updateInput : function()
12634     {
12635         var data = [];
12636         this.fileCollection.each(function(e) {
12637             data.push(e.data);
12638         });
12639         
12640         this.inputEl().dom.value = JSON.stringify(data);
12641     }
12642     
12643     
12644 });
12645
12646
12647 Roo.bootstrap.CardUploader.ID = -1;/*
12648  * Based on:
12649  * Ext JS Library 1.1.1
12650  * Copyright(c) 2006-2007, Ext JS, LLC.
12651  *
12652  * Originally Released Under LGPL - original licence link has changed is not relivant.
12653  *
12654  * Fork - LGPL
12655  * <script type="text/javascript">
12656  */
12657
12658
12659 /**
12660  * @class Roo.data.SortTypes
12661  * @singleton
12662  * Defines the default sorting (casting?) comparison functions used when sorting data.
12663  */
12664 Roo.data.SortTypes = {
12665     /**
12666      * Default sort that does nothing
12667      * @param {Mixed} s The value being converted
12668      * @return {Mixed} The comparison value
12669      */
12670     none : function(s){
12671         return s;
12672     },
12673     
12674     /**
12675      * The regular expression used to strip tags
12676      * @type {RegExp}
12677      * @property
12678      */
12679     stripTagsRE : /<\/?[^>]+>/gi,
12680     
12681     /**
12682      * Strips all HTML tags to sort on text only
12683      * @param {Mixed} s The value being converted
12684      * @return {String} The comparison value
12685      */
12686     asText : function(s){
12687         return String(s).replace(this.stripTagsRE, "");
12688     },
12689     
12690     /**
12691      * Strips all HTML tags to sort on text only - Case insensitive
12692      * @param {Mixed} s The value being converted
12693      * @return {String} The comparison value
12694      */
12695     asUCText : function(s){
12696         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12697     },
12698     
12699     /**
12700      * Case insensitive string
12701      * @param {Mixed} s The value being converted
12702      * @return {String} The comparison value
12703      */
12704     asUCString : function(s) {
12705         return String(s).toUpperCase();
12706     },
12707     
12708     /**
12709      * Date sorting
12710      * @param {Mixed} s The value being converted
12711      * @return {Number} The comparison value
12712      */
12713     asDate : function(s) {
12714         if(!s){
12715             return 0;
12716         }
12717         if(s instanceof Date){
12718             return s.getTime();
12719         }
12720         return Date.parse(String(s));
12721     },
12722     
12723     /**
12724      * Float sorting
12725      * @param {Mixed} s The value being converted
12726      * @return {Float} The comparison value
12727      */
12728     asFloat : function(s) {
12729         var val = parseFloat(String(s).replace(/,/g, ""));
12730         if(isNaN(val)) {
12731             val = 0;
12732         }
12733         return val;
12734     },
12735     
12736     /**
12737      * Integer sorting
12738      * @param {Mixed} s The value being converted
12739      * @return {Number} The comparison value
12740      */
12741     asInt : function(s) {
12742         var val = parseInt(String(s).replace(/,/g, ""));
12743         if(isNaN(val)) {
12744             val = 0;
12745         }
12746         return val;
12747     }
12748 };/*
12749  * Based on:
12750  * Ext JS Library 1.1.1
12751  * Copyright(c) 2006-2007, Ext JS, LLC.
12752  *
12753  * Originally Released Under LGPL - original licence link has changed is not relivant.
12754  *
12755  * Fork - LGPL
12756  * <script type="text/javascript">
12757  */
12758
12759 /**
12760 * @class Roo.data.Record
12761  * Instances of this class encapsulate both record <em>definition</em> information, and record
12762  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12763  * to access Records cached in an {@link Roo.data.Store} object.<br>
12764  * <p>
12765  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12766  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12767  * objects.<br>
12768  * <p>
12769  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12770  * @constructor
12771  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12772  * {@link #create}. The parameters are the same.
12773  * @param {Array} data An associative Array of data values keyed by the field name.
12774  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12775  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12776  * not specified an integer id is generated.
12777  */
12778 Roo.data.Record = function(data, id){
12779     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12780     this.data = data;
12781 };
12782
12783 /**
12784  * Generate a constructor for a specific record layout.
12785  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12786  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12787  * Each field definition object may contain the following properties: <ul>
12788  * <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,
12789  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12790  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12791  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12792  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12793  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12794  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12795  * this may be omitted.</p></li>
12796  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12797  * <ul><li>auto (Default, implies no conversion)</li>
12798  * <li>string</li>
12799  * <li>int</li>
12800  * <li>float</li>
12801  * <li>boolean</li>
12802  * <li>date</li></ul></p></li>
12803  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12804  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12805  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12806  * by the Reader into an object that will be stored in the Record. It is passed the
12807  * following parameters:<ul>
12808  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12809  * </ul></p></li>
12810  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12811  * </ul>
12812  * <br>usage:<br><pre><code>
12813 var TopicRecord = Roo.data.Record.create(
12814     {name: 'title', mapping: 'topic_title'},
12815     {name: 'author', mapping: 'username'},
12816     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12817     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12818     {name: 'lastPoster', mapping: 'user2'},
12819     {name: 'excerpt', mapping: 'post_text'}
12820 );
12821
12822 var myNewRecord = new TopicRecord({
12823     title: 'Do my job please',
12824     author: 'noobie',
12825     totalPosts: 1,
12826     lastPost: new Date(),
12827     lastPoster: 'Animal',
12828     excerpt: 'No way dude!'
12829 });
12830 myStore.add(myNewRecord);
12831 </code></pre>
12832  * @method create
12833  * @static
12834  */
12835 Roo.data.Record.create = function(o){
12836     var f = function(){
12837         f.superclass.constructor.apply(this, arguments);
12838     };
12839     Roo.extend(f, Roo.data.Record);
12840     var p = f.prototype;
12841     p.fields = new Roo.util.MixedCollection(false, function(field){
12842         return field.name;
12843     });
12844     for(var i = 0, len = o.length; i < len; i++){
12845         p.fields.add(new Roo.data.Field(o[i]));
12846     }
12847     f.getField = function(name){
12848         return p.fields.get(name);  
12849     };
12850     return f;
12851 };
12852
12853 Roo.data.Record.AUTO_ID = 1000;
12854 Roo.data.Record.EDIT = 'edit';
12855 Roo.data.Record.REJECT = 'reject';
12856 Roo.data.Record.COMMIT = 'commit';
12857
12858 Roo.data.Record.prototype = {
12859     /**
12860      * Readonly flag - true if this record has been modified.
12861      * @type Boolean
12862      */
12863     dirty : false,
12864     editing : false,
12865     error: null,
12866     modified: null,
12867
12868     // private
12869     join : function(store){
12870         this.store = store;
12871     },
12872
12873     /**
12874      * Set the named field to the specified value.
12875      * @param {String} name The name of the field to set.
12876      * @param {Object} value The value to set the field to.
12877      */
12878     set : function(name, value){
12879         if(this.data[name] == value){
12880             return;
12881         }
12882         this.dirty = true;
12883         if(!this.modified){
12884             this.modified = {};
12885         }
12886         if(typeof this.modified[name] == 'undefined'){
12887             this.modified[name] = this.data[name];
12888         }
12889         this.data[name] = value;
12890         if(!this.editing && this.store){
12891             this.store.afterEdit(this);
12892         }       
12893     },
12894
12895     /**
12896      * Get the value of the named field.
12897      * @param {String} name The name of the field to get the value of.
12898      * @return {Object} The value of the field.
12899      */
12900     get : function(name){
12901         return this.data[name]; 
12902     },
12903
12904     // private
12905     beginEdit : function(){
12906         this.editing = true;
12907         this.modified = {}; 
12908     },
12909
12910     // private
12911     cancelEdit : function(){
12912         this.editing = false;
12913         delete this.modified;
12914     },
12915
12916     // private
12917     endEdit : function(){
12918         this.editing = false;
12919         if(this.dirty && this.store){
12920             this.store.afterEdit(this);
12921         }
12922     },
12923
12924     /**
12925      * Usually called by the {@link Roo.data.Store} which owns the Record.
12926      * Rejects all changes made to the Record since either creation, or the last commit operation.
12927      * Modified fields are reverted to their original values.
12928      * <p>
12929      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12930      * of reject operations.
12931      */
12932     reject : function(){
12933         var m = this.modified;
12934         for(var n in m){
12935             if(typeof m[n] != "function"){
12936                 this.data[n] = m[n];
12937             }
12938         }
12939         this.dirty = false;
12940         delete this.modified;
12941         this.editing = false;
12942         if(this.store){
12943             this.store.afterReject(this);
12944         }
12945     },
12946
12947     /**
12948      * Usually called by the {@link Roo.data.Store} which owns the Record.
12949      * Commits all changes made to the Record since either creation, or the last commit operation.
12950      * <p>
12951      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12952      * of commit operations.
12953      */
12954     commit : function(){
12955         this.dirty = false;
12956         delete this.modified;
12957         this.editing = false;
12958         if(this.store){
12959             this.store.afterCommit(this);
12960         }
12961     },
12962
12963     // private
12964     hasError : function(){
12965         return this.error != null;
12966     },
12967
12968     // private
12969     clearError : function(){
12970         this.error = null;
12971     },
12972
12973     /**
12974      * Creates a copy of this record.
12975      * @param {String} id (optional) A new record id if you don't want to use this record's id
12976      * @return {Record}
12977      */
12978     copy : function(newId) {
12979         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12980     }
12981 };/*
12982  * Based on:
12983  * Ext JS Library 1.1.1
12984  * Copyright(c) 2006-2007, Ext JS, LLC.
12985  *
12986  * Originally Released Under LGPL - original licence link has changed is not relivant.
12987  *
12988  * Fork - LGPL
12989  * <script type="text/javascript">
12990  */
12991
12992
12993
12994 /**
12995  * @class Roo.data.Store
12996  * @extends Roo.util.Observable
12997  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12998  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12999  * <p>
13000  * 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
13001  * has no knowledge of the format of the data returned by the Proxy.<br>
13002  * <p>
13003  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13004  * instances from the data object. These records are cached and made available through accessor functions.
13005  * @constructor
13006  * Creates a new Store.
13007  * @param {Object} config A config object containing the objects needed for the Store to access data,
13008  * and read the data into Records.
13009  */
13010 Roo.data.Store = function(config){
13011     this.data = new Roo.util.MixedCollection(false);
13012     this.data.getKey = function(o){
13013         return o.id;
13014     };
13015     this.baseParams = {};
13016     // private
13017     this.paramNames = {
13018         "start" : "start",
13019         "limit" : "limit",
13020         "sort" : "sort",
13021         "dir" : "dir",
13022         "multisort" : "_multisort"
13023     };
13024
13025     if(config && config.data){
13026         this.inlineData = config.data;
13027         delete config.data;
13028     }
13029
13030     Roo.apply(this, config);
13031     
13032     if(this.reader){ // reader passed
13033         this.reader = Roo.factory(this.reader, Roo.data);
13034         this.reader.xmodule = this.xmodule || false;
13035         if(!this.recordType){
13036             this.recordType = this.reader.recordType;
13037         }
13038         if(this.reader.onMetaChange){
13039             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13040         }
13041     }
13042
13043     if(this.recordType){
13044         this.fields = this.recordType.prototype.fields;
13045     }
13046     this.modified = [];
13047
13048     this.addEvents({
13049         /**
13050          * @event datachanged
13051          * Fires when the data cache has changed, and a widget which is using this Store
13052          * as a Record cache should refresh its view.
13053          * @param {Store} this
13054          */
13055         datachanged : true,
13056         /**
13057          * @event metachange
13058          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13059          * @param {Store} this
13060          * @param {Object} meta The JSON metadata
13061          */
13062         metachange : true,
13063         /**
13064          * @event add
13065          * Fires when Records have been added to the Store
13066          * @param {Store} this
13067          * @param {Roo.data.Record[]} records The array of Records added
13068          * @param {Number} index The index at which the record(s) were added
13069          */
13070         add : true,
13071         /**
13072          * @event remove
13073          * Fires when a Record has been removed from the Store
13074          * @param {Store} this
13075          * @param {Roo.data.Record} record The Record that was removed
13076          * @param {Number} index The index at which the record was removed
13077          */
13078         remove : true,
13079         /**
13080          * @event update
13081          * Fires when a Record has been updated
13082          * @param {Store} this
13083          * @param {Roo.data.Record} record The Record that was updated
13084          * @param {String} operation The update operation being performed.  Value may be one of:
13085          * <pre><code>
13086  Roo.data.Record.EDIT
13087  Roo.data.Record.REJECT
13088  Roo.data.Record.COMMIT
13089          * </code></pre>
13090          */
13091         update : true,
13092         /**
13093          * @event clear
13094          * Fires when the data cache has been cleared.
13095          * @param {Store} this
13096          */
13097         clear : true,
13098         /**
13099          * @event beforeload
13100          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13101          * the load action will be canceled.
13102          * @param {Store} this
13103          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13104          */
13105         beforeload : true,
13106         /**
13107          * @event beforeloadadd
13108          * Fires after a new set of Records has been loaded.
13109          * @param {Store} this
13110          * @param {Roo.data.Record[]} records The Records that were loaded
13111          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13112          */
13113         beforeloadadd : true,
13114         /**
13115          * @event load
13116          * Fires after a new set of Records has been loaded, before they are added to the store.
13117          * @param {Store} this
13118          * @param {Roo.data.Record[]} records The Records that were loaded
13119          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13120          * @params {Object} return from reader
13121          */
13122         load : true,
13123         /**
13124          * @event loadexception
13125          * Fires if an exception occurs in the Proxy during loading.
13126          * Called with the signature of the Proxy's "loadexception" event.
13127          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13128          * 
13129          * @param {Proxy} 
13130          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13131          * @param {Object} load options 
13132          * @param {Object} jsonData from your request (normally this contains the Exception)
13133          */
13134         loadexception : true
13135     });
13136     
13137     if(this.proxy){
13138         this.proxy = Roo.factory(this.proxy, Roo.data);
13139         this.proxy.xmodule = this.xmodule || false;
13140         this.relayEvents(this.proxy,  ["loadexception"]);
13141     }
13142     this.sortToggle = {};
13143     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13144
13145     Roo.data.Store.superclass.constructor.call(this);
13146
13147     if(this.inlineData){
13148         this.loadData(this.inlineData);
13149         delete this.inlineData;
13150     }
13151 };
13152
13153 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13154      /**
13155     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13156     * without a remote query - used by combo/forms at present.
13157     */
13158     
13159     /**
13160     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13161     */
13162     /**
13163     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13164     */
13165     /**
13166     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13167     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13168     */
13169     /**
13170     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13171     * on any HTTP request
13172     */
13173     /**
13174     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13175     */
13176     /**
13177     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13178     */
13179     multiSort: false,
13180     /**
13181     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13182     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13183     */
13184     remoteSort : false,
13185
13186     /**
13187     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13188      * loaded or when a record is removed. (defaults to false).
13189     */
13190     pruneModifiedRecords : false,
13191
13192     // private
13193     lastOptions : null,
13194
13195     /**
13196      * Add Records to the Store and fires the add event.
13197      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13198      */
13199     add : function(records){
13200         records = [].concat(records);
13201         for(var i = 0, len = records.length; i < len; i++){
13202             records[i].join(this);
13203         }
13204         var index = this.data.length;
13205         this.data.addAll(records);
13206         this.fireEvent("add", this, records, index);
13207     },
13208
13209     /**
13210      * Remove a Record from the Store and fires the remove event.
13211      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13212      */
13213     remove : function(record){
13214         var index = this.data.indexOf(record);
13215         this.data.removeAt(index);
13216  
13217         if(this.pruneModifiedRecords){
13218             this.modified.remove(record);
13219         }
13220         this.fireEvent("remove", this, record, index);
13221     },
13222
13223     /**
13224      * Remove all Records from the Store and fires the clear event.
13225      */
13226     removeAll : function(){
13227         this.data.clear();
13228         if(this.pruneModifiedRecords){
13229             this.modified = [];
13230         }
13231         this.fireEvent("clear", this);
13232     },
13233
13234     /**
13235      * Inserts Records to the Store at the given index and fires the add event.
13236      * @param {Number} index The start index at which to insert the passed Records.
13237      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13238      */
13239     insert : function(index, records){
13240         records = [].concat(records);
13241         for(var i = 0, len = records.length; i < len; i++){
13242             this.data.insert(index, records[i]);
13243             records[i].join(this);
13244         }
13245         this.fireEvent("add", this, records, index);
13246     },
13247
13248     /**
13249      * Get the index within the cache of the passed Record.
13250      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13251      * @return {Number} The index of the passed Record. Returns -1 if not found.
13252      */
13253     indexOf : function(record){
13254         return this.data.indexOf(record);
13255     },
13256
13257     /**
13258      * Get the index within the cache of the Record with the passed id.
13259      * @param {String} id The id of the Record to find.
13260      * @return {Number} The index of the Record. Returns -1 if not found.
13261      */
13262     indexOfId : function(id){
13263         return this.data.indexOfKey(id);
13264     },
13265
13266     /**
13267      * Get the Record with the specified id.
13268      * @param {String} id The id of the Record to find.
13269      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13270      */
13271     getById : function(id){
13272         return this.data.key(id);
13273     },
13274
13275     /**
13276      * Get the Record at the specified index.
13277      * @param {Number} index The index of the Record to find.
13278      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13279      */
13280     getAt : function(index){
13281         return this.data.itemAt(index);
13282     },
13283
13284     /**
13285      * Returns a range of Records between specified indices.
13286      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13287      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13288      * @return {Roo.data.Record[]} An array of Records
13289      */
13290     getRange : function(start, end){
13291         return this.data.getRange(start, end);
13292     },
13293
13294     // private
13295     storeOptions : function(o){
13296         o = Roo.apply({}, o);
13297         delete o.callback;
13298         delete o.scope;
13299         this.lastOptions = o;
13300     },
13301
13302     /**
13303      * Loads the Record cache from the configured Proxy using the configured Reader.
13304      * <p>
13305      * If using remote paging, then the first load call must specify the <em>start</em>
13306      * and <em>limit</em> properties in the options.params property to establish the initial
13307      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13308      * <p>
13309      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13310      * and this call will return before the new data has been loaded. Perform any post-processing
13311      * in a callback function, or in a "load" event handler.</strong>
13312      * <p>
13313      * @param {Object} options An object containing properties which control loading options:<ul>
13314      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13315      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13316      * passed the following arguments:<ul>
13317      * <li>r : Roo.data.Record[]</li>
13318      * <li>options: Options object from the load call</li>
13319      * <li>success: Boolean success indicator</li></ul></li>
13320      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13321      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13322      * </ul>
13323      */
13324     load : function(options){
13325         options = options || {};
13326         if(this.fireEvent("beforeload", this, options) !== false){
13327             this.storeOptions(options);
13328             var p = Roo.apply(options.params || {}, this.baseParams);
13329             // if meta was not loaded from remote source.. try requesting it.
13330             if (!this.reader.metaFromRemote) {
13331                 p._requestMeta = 1;
13332             }
13333             if(this.sortInfo && this.remoteSort){
13334                 var pn = this.paramNames;
13335                 p[pn["sort"]] = this.sortInfo.field;
13336                 p[pn["dir"]] = this.sortInfo.direction;
13337             }
13338             if (this.multiSort) {
13339                 var pn = this.paramNames;
13340                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13341             }
13342             
13343             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13344         }
13345     },
13346
13347     /**
13348      * Reloads the Record cache from the configured Proxy using the configured Reader and
13349      * the options from the last load operation performed.
13350      * @param {Object} options (optional) An object containing properties which may override the options
13351      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13352      * the most recently used options are reused).
13353      */
13354     reload : function(options){
13355         this.load(Roo.applyIf(options||{}, this.lastOptions));
13356     },
13357
13358     // private
13359     // Called as a callback by the Reader during a load operation.
13360     loadRecords : function(o, options, success){
13361         if(!o || success === false){
13362             if(success !== false){
13363                 this.fireEvent("load", this, [], options, o);
13364             }
13365             if(options.callback){
13366                 options.callback.call(options.scope || this, [], options, false);
13367             }
13368             return;
13369         }
13370         // if data returned failure - throw an exception.
13371         if (o.success === false) {
13372             // show a message if no listener is registered.
13373             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13374                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13375             }
13376             // loadmask wil be hooked into this..
13377             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13378             return;
13379         }
13380         var r = o.records, t = o.totalRecords || r.length;
13381         
13382         this.fireEvent("beforeloadadd", this, r, options, o);
13383         
13384         if(!options || options.add !== true){
13385             if(this.pruneModifiedRecords){
13386                 this.modified = [];
13387             }
13388             for(var i = 0, len = r.length; i < len; i++){
13389                 r[i].join(this);
13390             }
13391             if(this.snapshot){
13392                 this.data = this.snapshot;
13393                 delete this.snapshot;
13394             }
13395             this.data.clear();
13396             this.data.addAll(r);
13397             this.totalLength = t;
13398             this.applySort();
13399             this.fireEvent("datachanged", this);
13400         }else{
13401             this.totalLength = Math.max(t, this.data.length+r.length);
13402             this.add(r);
13403         }
13404         
13405         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13406                 
13407             var e = new Roo.data.Record({});
13408
13409             e.set(this.parent.displayField, this.parent.emptyTitle);
13410             e.set(this.parent.valueField, '');
13411
13412             this.insert(0, e);
13413         }
13414             
13415         this.fireEvent("load", this, r, options, o);
13416         if(options.callback){
13417             options.callback.call(options.scope || this, r, options, true);
13418         }
13419     },
13420
13421
13422     /**
13423      * Loads data from a passed data block. A Reader which understands the format of the data
13424      * must have been configured in the constructor.
13425      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13426      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13427      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13428      */
13429     loadData : function(o, append){
13430         var r = this.reader.readRecords(o);
13431         this.loadRecords(r, {add: append}, true);
13432     },
13433     
13434      /**
13435      * using 'cn' the nested child reader read the child array into it's child stores.
13436      * @param {Object} rec The record with a 'children array
13437      */
13438     loadDataFromChildren : function(rec)
13439     {
13440         this.loadData(this.reader.toLoadData(rec));
13441     },
13442     
13443
13444     /**
13445      * Gets the number of cached records.
13446      * <p>
13447      * <em>If using paging, this may not be the total size of the dataset. If the data object
13448      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13449      * the data set size</em>
13450      */
13451     getCount : function(){
13452         return this.data.length || 0;
13453     },
13454
13455     /**
13456      * Gets the total number of records in the dataset as returned by the server.
13457      * <p>
13458      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13459      * the dataset size</em>
13460      */
13461     getTotalCount : function(){
13462         return this.totalLength || 0;
13463     },
13464
13465     /**
13466      * Returns the sort state of the Store as an object with two properties:
13467      * <pre><code>
13468  field {String} The name of the field by which the Records are sorted
13469  direction {String} The sort order, "ASC" or "DESC"
13470      * </code></pre>
13471      */
13472     getSortState : function(){
13473         return this.sortInfo;
13474     },
13475
13476     // private
13477     applySort : function(){
13478         if(this.sortInfo && !this.remoteSort){
13479             var s = this.sortInfo, f = s.field;
13480             var st = this.fields.get(f).sortType;
13481             var fn = function(r1, r2){
13482                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13483                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13484             };
13485             this.data.sort(s.direction, fn);
13486             if(this.snapshot && this.snapshot != this.data){
13487                 this.snapshot.sort(s.direction, fn);
13488             }
13489         }
13490     },
13491
13492     /**
13493      * Sets the default sort column and order to be used by the next load operation.
13494      * @param {String} fieldName The name of the field to sort by.
13495      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13496      */
13497     setDefaultSort : function(field, dir){
13498         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13499     },
13500
13501     /**
13502      * Sort the Records.
13503      * If remote sorting is used, the sort is performed on the server, and the cache is
13504      * reloaded. If local sorting is used, the cache is sorted internally.
13505      * @param {String} fieldName The name of the field to sort by.
13506      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13507      */
13508     sort : function(fieldName, dir){
13509         var f = this.fields.get(fieldName);
13510         if(!dir){
13511             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13512             
13513             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13514                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13515             }else{
13516                 dir = f.sortDir;
13517             }
13518         }
13519         this.sortToggle[f.name] = dir;
13520         this.sortInfo = {field: f.name, direction: dir};
13521         if(!this.remoteSort){
13522             this.applySort();
13523             this.fireEvent("datachanged", this);
13524         }else{
13525             this.load(this.lastOptions);
13526         }
13527     },
13528
13529     /**
13530      * Calls the specified function for each of the Records in the cache.
13531      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13532      * Returning <em>false</em> aborts and exits the iteration.
13533      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13534      */
13535     each : function(fn, scope){
13536         this.data.each(fn, scope);
13537     },
13538
13539     /**
13540      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13541      * (e.g., during paging).
13542      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13543      */
13544     getModifiedRecords : function(){
13545         return this.modified;
13546     },
13547
13548     // private
13549     createFilterFn : function(property, value, anyMatch){
13550         if(!value.exec){ // not a regex
13551             value = String(value);
13552             if(value.length == 0){
13553                 return false;
13554             }
13555             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13556         }
13557         return function(r){
13558             return value.test(r.data[property]);
13559         };
13560     },
13561
13562     /**
13563      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13564      * @param {String} property A field on your records
13565      * @param {Number} start The record index to start at (defaults to 0)
13566      * @param {Number} end The last record index to include (defaults to length - 1)
13567      * @return {Number} The sum
13568      */
13569     sum : function(property, start, end){
13570         var rs = this.data.items, v = 0;
13571         start = start || 0;
13572         end = (end || end === 0) ? end : rs.length-1;
13573
13574         for(var i = start; i <= end; i++){
13575             v += (rs[i].data[property] || 0);
13576         }
13577         return v;
13578     },
13579
13580     /**
13581      * Filter the records by a specified property.
13582      * @param {String} field A field on your records
13583      * @param {String/RegExp} value Either a string that the field
13584      * should start with or a RegExp to test against the field
13585      * @param {Boolean} anyMatch True to match any part not just the beginning
13586      */
13587     filter : function(property, value, anyMatch){
13588         var fn = this.createFilterFn(property, value, anyMatch);
13589         return fn ? this.filterBy(fn) : this.clearFilter();
13590     },
13591
13592     /**
13593      * Filter by a function. The specified function will be called with each
13594      * record in this data source. If the function returns true the record is included,
13595      * otherwise it is filtered.
13596      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13597      * @param {Object} scope (optional) The scope of the function (defaults to this)
13598      */
13599     filterBy : function(fn, scope){
13600         this.snapshot = this.snapshot || this.data;
13601         this.data = this.queryBy(fn, scope||this);
13602         this.fireEvent("datachanged", this);
13603     },
13604
13605     /**
13606      * Query the records by a specified property.
13607      * @param {String} field A field on your records
13608      * @param {String/RegExp} value Either a string that the field
13609      * should start with or a RegExp to test against the field
13610      * @param {Boolean} anyMatch True to match any part not just the beginning
13611      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13612      */
13613     query : function(property, value, anyMatch){
13614         var fn = this.createFilterFn(property, value, anyMatch);
13615         return fn ? this.queryBy(fn) : this.data.clone();
13616     },
13617
13618     /**
13619      * Query by a function. The specified function will be called with each
13620      * record in this data source. If the function returns true the record is included
13621      * in the results.
13622      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13623      * @param {Object} scope (optional) The scope of the function (defaults to this)
13624       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13625      **/
13626     queryBy : function(fn, scope){
13627         var data = this.snapshot || this.data;
13628         return data.filterBy(fn, scope||this);
13629     },
13630
13631     /**
13632      * Collects unique values for a particular dataIndex from this store.
13633      * @param {String} dataIndex The property to collect
13634      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13635      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13636      * @return {Array} An array of the unique values
13637      **/
13638     collect : function(dataIndex, allowNull, bypassFilter){
13639         var d = (bypassFilter === true && this.snapshot) ?
13640                 this.snapshot.items : this.data.items;
13641         var v, sv, r = [], l = {};
13642         for(var i = 0, len = d.length; i < len; i++){
13643             v = d[i].data[dataIndex];
13644             sv = String(v);
13645             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13646                 l[sv] = true;
13647                 r[r.length] = v;
13648             }
13649         }
13650         return r;
13651     },
13652
13653     /**
13654      * Revert to a view of the Record cache with no filtering applied.
13655      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13656      */
13657     clearFilter : function(suppressEvent){
13658         if(this.snapshot && this.snapshot != this.data){
13659             this.data = this.snapshot;
13660             delete this.snapshot;
13661             if(suppressEvent !== true){
13662                 this.fireEvent("datachanged", this);
13663             }
13664         }
13665     },
13666
13667     // private
13668     afterEdit : function(record){
13669         if(this.modified.indexOf(record) == -1){
13670             this.modified.push(record);
13671         }
13672         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13673     },
13674     
13675     // private
13676     afterReject : function(record){
13677         this.modified.remove(record);
13678         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13679     },
13680
13681     // private
13682     afterCommit : function(record){
13683         this.modified.remove(record);
13684         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13685     },
13686
13687     /**
13688      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13689      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13690      */
13691     commitChanges : function(){
13692         var m = this.modified.slice(0);
13693         this.modified = [];
13694         for(var i = 0, len = m.length; i < len; i++){
13695             m[i].commit();
13696         }
13697     },
13698
13699     /**
13700      * Cancel outstanding changes on all changed records.
13701      */
13702     rejectChanges : function(){
13703         var m = this.modified.slice(0);
13704         this.modified = [];
13705         for(var i = 0, len = m.length; i < len; i++){
13706             m[i].reject();
13707         }
13708     },
13709
13710     onMetaChange : function(meta, rtype, o){
13711         this.recordType = rtype;
13712         this.fields = rtype.prototype.fields;
13713         delete this.snapshot;
13714         this.sortInfo = meta.sortInfo || this.sortInfo;
13715         this.modified = [];
13716         this.fireEvent('metachange', this, this.reader.meta);
13717     },
13718     
13719     moveIndex : function(data, type)
13720     {
13721         var index = this.indexOf(data);
13722         
13723         var newIndex = index + type;
13724         
13725         this.remove(data);
13726         
13727         this.insert(newIndex, data);
13728         
13729     }
13730 });/*
13731  * Based on:
13732  * Ext JS Library 1.1.1
13733  * Copyright(c) 2006-2007, Ext JS, LLC.
13734  *
13735  * Originally Released Under LGPL - original licence link has changed is not relivant.
13736  *
13737  * Fork - LGPL
13738  * <script type="text/javascript">
13739  */
13740
13741 /**
13742  * @class Roo.data.SimpleStore
13743  * @extends Roo.data.Store
13744  * Small helper class to make creating Stores from Array data easier.
13745  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13746  * @cfg {Array} fields An array of field definition objects, or field name strings.
13747  * @cfg {Object} an existing reader (eg. copied from another store)
13748  * @cfg {Array} data The multi-dimensional array of data
13749  * @constructor
13750  * @param {Object} config
13751  */
13752 Roo.data.SimpleStore = function(config)
13753 {
13754     Roo.data.SimpleStore.superclass.constructor.call(this, {
13755         isLocal : true,
13756         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13757                 id: config.id
13758             },
13759             Roo.data.Record.create(config.fields)
13760         ),
13761         proxy : new Roo.data.MemoryProxy(config.data)
13762     });
13763     this.load();
13764 };
13765 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13766  * Based on:
13767  * Ext JS Library 1.1.1
13768  * Copyright(c) 2006-2007, Ext JS, LLC.
13769  *
13770  * Originally Released Under LGPL - original licence link has changed is not relivant.
13771  *
13772  * Fork - LGPL
13773  * <script type="text/javascript">
13774  */
13775
13776 /**
13777 /**
13778  * @extends Roo.data.Store
13779  * @class Roo.data.JsonStore
13780  * Small helper class to make creating Stores for JSON data easier. <br/>
13781 <pre><code>
13782 var store = new Roo.data.JsonStore({
13783     url: 'get-images.php',
13784     root: 'images',
13785     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13786 });
13787 </code></pre>
13788  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13789  * JsonReader and HttpProxy (unless inline data is provided).</b>
13790  * @cfg {Array} fields An array of field definition objects, or field name strings.
13791  * @constructor
13792  * @param {Object} config
13793  */
13794 Roo.data.JsonStore = function(c){
13795     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13796         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13797         reader: new Roo.data.JsonReader(c, c.fields)
13798     }));
13799 };
13800 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13801  * Based on:
13802  * Ext JS Library 1.1.1
13803  * Copyright(c) 2006-2007, Ext JS, LLC.
13804  *
13805  * Originally Released Under LGPL - original licence link has changed is not relivant.
13806  *
13807  * Fork - LGPL
13808  * <script type="text/javascript">
13809  */
13810
13811  
13812 Roo.data.Field = function(config){
13813     if(typeof config == "string"){
13814         config = {name: config};
13815     }
13816     Roo.apply(this, config);
13817     
13818     if(!this.type){
13819         this.type = "auto";
13820     }
13821     
13822     var st = Roo.data.SortTypes;
13823     // named sortTypes are supported, here we look them up
13824     if(typeof this.sortType == "string"){
13825         this.sortType = st[this.sortType];
13826     }
13827     
13828     // set default sortType for strings and dates
13829     if(!this.sortType){
13830         switch(this.type){
13831             case "string":
13832                 this.sortType = st.asUCString;
13833                 break;
13834             case "date":
13835                 this.sortType = st.asDate;
13836                 break;
13837             default:
13838                 this.sortType = st.none;
13839         }
13840     }
13841
13842     // define once
13843     var stripRe = /[\$,%]/g;
13844
13845     // prebuilt conversion function for this field, instead of
13846     // switching every time we're reading a value
13847     if(!this.convert){
13848         var cv, dateFormat = this.dateFormat;
13849         switch(this.type){
13850             case "":
13851             case "auto":
13852             case undefined:
13853                 cv = function(v){ return v; };
13854                 break;
13855             case "string":
13856                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13857                 break;
13858             case "int":
13859                 cv = function(v){
13860                     return v !== undefined && v !== null && v !== '' ?
13861                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13862                     };
13863                 break;
13864             case "float":
13865                 cv = function(v){
13866                     return v !== undefined && v !== null && v !== '' ?
13867                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13868                     };
13869                 break;
13870             case "bool":
13871             case "boolean":
13872                 cv = function(v){ return v === true || v === "true" || v == 1; };
13873                 break;
13874             case "date":
13875                 cv = function(v){
13876                     if(!v){
13877                         return '';
13878                     }
13879                     if(v instanceof Date){
13880                         return v;
13881                     }
13882                     if(dateFormat){
13883                         if(dateFormat == "timestamp"){
13884                             return new Date(v*1000);
13885                         }
13886                         return Date.parseDate(v, dateFormat);
13887                     }
13888                     var parsed = Date.parse(v);
13889                     return parsed ? new Date(parsed) : null;
13890                 };
13891              break;
13892             
13893         }
13894         this.convert = cv;
13895     }
13896 };
13897
13898 Roo.data.Field.prototype = {
13899     dateFormat: null,
13900     defaultValue: "",
13901     mapping: null,
13902     sortType : null,
13903     sortDir : "ASC"
13904 };/*
13905  * Based on:
13906  * Ext JS Library 1.1.1
13907  * Copyright(c) 2006-2007, Ext JS, LLC.
13908  *
13909  * Originally Released Under LGPL - original licence link has changed is not relivant.
13910  *
13911  * Fork - LGPL
13912  * <script type="text/javascript">
13913  */
13914  
13915 // Base class for reading structured data from a data source.  This class is intended to be
13916 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13917
13918 /**
13919  * @class Roo.data.DataReader
13920  * Base class for reading structured data from a data source.  This class is intended to be
13921  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13922  */
13923
13924 Roo.data.DataReader = function(meta, recordType){
13925     
13926     this.meta = meta;
13927     
13928     this.recordType = recordType instanceof Array ? 
13929         Roo.data.Record.create(recordType) : recordType;
13930 };
13931
13932 Roo.data.DataReader.prototype = {
13933     
13934     
13935     readerType : 'Data',
13936      /**
13937      * Create an empty record
13938      * @param {Object} data (optional) - overlay some values
13939      * @return {Roo.data.Record} record created.
13940      */
13941     newRow :  function(d) {
13942         var da =  {};
13943         this.recordType.prototype.fields.each(function(c) {
13944             switch( c.type) {
13945                 case 'int' : da[c.name] = 0; break;
13946                 case 'date' : da[c.name] = new Date(); break;
13947                 case 'float' : da[c.name] = 0.0; break;
13948                 case 'boolean' : da[c.name] = false; break;
13949                 default : da[c.name] = ""; break;
13950             }
13951             
13952         });
13953         return new this.recordType(Roo.apply(da, d));
13954     }
13955     
13956     
13957 };/*
13958  * Based on:
13959  * Ext JS Library 1.1.1
13960  * Copyright(c) 2006-2007, Ext JS, LLC.
13961  *
13962  * Originally Released Under LGPL - original licence link has changed is not relivant.
13963  *
13964  * Fork - LGPL
13965  * <script type="text/javascript">
13966  */
13967
13968 /**
13969  * @class Roo.data.DataProxy
13970  * @extends Roo.data.Observable
13971  * This class is an abstract base class for implementations which provide retrieval of
13972  * unformatted data objects.<br>
13973  * <p>
13974  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13975  * (of the appropriate type which knows how to parse the data object) to provide a block of
13976  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13977  * <p>
13978  * Custom implementations must implement the load method as described in
13979  * {@link Roo.data.HttpProxy#load}.
13980  */
13981 Roo.data.DataProxy = function(){
13982     this.addEvents({
13983         /**
13984          * @event beforeload
13985          * Fires before a network request is made to retrieve a data object.
13986          * @param {Object} This DataProxy object.
13987          * @param {Object} params The params parameter to the load function.
13988          */
13989         beforeload : true,
13990         /**
13991          * @event load
13992          * Fires before the load method's callback is called.
13993          * @param {Object} This DataProxy object.
13994          * @param {Object} o The data object.
13995          * @param {Object} arg The callback argument object passed to the load function.
13996          */
13997         load : true,
13998         /**
13999          * @event loadexception
14000          * Fires if an Exception occurs during data retrieval.
14001          * @param {Object} This DataProxy object.
14002          * @param {Object} o The data object.
14003          * @param {Object} arg The callback argument object passed to the load function.
14004          * @param {Object} e The Exception.
14005          */
14006         loadexception : true
14007     });
14008     Roo.data.DataProxy.superclass.constructor.call(this);
14009 };
14010
14011 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14012
14013     /**
14014      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14015      */
14016 /*
14017  * Based on:
14018  * Ext JS Library 1.1.1
14019  * Copyright(c) 2006-2007, Ext JS, LLC.
14020  *
14021  * Originally Released Under LGPL - original licence link has changed is not relivant.
14022  *
14023  * Fork - LGPL
14024  * <script type="text/javascript">
14025  */
14026 /**
14027  * @class Roo.data.MemoryProxy
14028  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14029  * to the Reader when its load method is called.
14030  * @constructor
14031  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14032  */
14033 Roo.data.MemoryProxy = function(data){
14034     if (data.data) {
14035         data = data.data;
14036     }
14037     Roo.data.MemoryProxy.superclass.constructor.call(this);
14038     this.data = data;
14039 };
14040
14041 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14042     
14043     /**
14044      * Load data from the requested source (in this case an in-memory
14045      * data object passed to the constructor), read the data object into
14046      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14047      * process that block using the passed callback.
14048      * @param {Object} params This parameter is not used by the MemoryProxy class.
14049      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14050      * object into a block of Roo.data.Records.
14051      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14052      * The function must be passed <ul>
14053      * <li>The Record block object</li>
14054      * <li>The "arg" argument from the load function</li>
14055      * <li>A boolean success indicator</li>
14056      * </ul>
14057      * @param {Object} scope The scope in which to call the callback
14058      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14059      */
14060     load : function(params, reader, callback, scope, arg){
14061         params = params || {};
14062         var result;
14063         try {
14064             result = reader.readRecords(params.data ? params.data :this.data);
14065         }catch(e){
14066             this.fireEvent("loadexception", this, arg, null, e);
14067             callback.call(scope, null, arg, false);
14068             return;
14069         }
14070         callback.call(scope, result, arg, true);
14071     },
14072     
14073     // private
14074     update : function(params, records){
14075         
14076     }
14077 });/*
14078  * Based on:
14079  * Ext JS Library 1.1.1
14080  * Copyright(c) 2006-2007, Ext JS, LLC.
14081  *
14082  * Originally Released Under LGPL - original licence link has changed is not relivant.
14083  *
14084  * Fork - LGPL
14085  * <script type="text/javascript">
14086  */
14087 /**
14088  * @class Roo.data.HttpProxy
14089  * @extends Roo.data.DataProxy
14090  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14091  * configured to reference a certain URL.<br><br>
14092  * <p>
14093  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14094  * from which the running page was served.<br><br>
14095  * <p>
14096  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14097  * <p>
14098  * Be aware that to enable the browser to parse an XML document, the server must set
14099  * the Content-Type header in the HTTP response to "text/xml".
14100  * @constructor
14101  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14102  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14103  * will be used to make the request.
14104  */
14105 Roo.data.HttpProxy = function(conn){
14106     Roo.data.HttpProxy.superclass.constructor.call(this);
14107     // is conn a conn config or a real conn?
14108     this.conn = conn;
14109     this.useAjax = !conn || !conn.events;
14110   
14111 };
14112
14113 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14114     // thse are take from connection...
14115     
14116     /**
14117      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14118      */
14119     /**
14120      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14121      * extra parameters to each request made by this object. (defaults to undefined)
14122      */
14123     /**
14124      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14125      *  to each request made by this object. (defaults to undefined)
14126      */
14127     /**
14128      * @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)
14129      */
14130     /**
14131      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14132      */
14133      /**
14134      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14135      * @type Boolean
14136      */
14137   
14138
14139     /**
14140      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14141      * @type Boolean
14142      */
14143     /**
14144      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14145      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14146      * a finer-grained basis than the DataProxy events.
14147      */
14148     getConnection : function(){
14149         return this.useAjax ? Roo.Ajax : this.conn;
14150     },
14151
14152     /**
14153      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14154      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14155      * process that block using the passed callback.
14156      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14157      * for the request to the remote server.
14158      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14159      * object into a block of Roo.data.Records.
14160      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14161      * The function must be passed <ul>
14162      * <li>The Record block object</li>
14163      * <li>The "arg" argument from the load function</li>
14164      * <li>A boolean success indicator</li>
14165      * </ul>
14166      * @param {Object} scope The scope in which to call the callback
14167      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14168      */
14169     load : function(params, reader, callback, scope, arg){
14170         if(this.fireEvent("beforeload", this, params) !== false){
14171             var  o = {
14172                 params : params || {},
14173                 request: {
14174                     callback : callback,
14175                     scope : scope,
14176                     arg : arg
14177                 },
14178                 reader: reader,
14179                 callback : this.loadResponse,
14180                 scope: this
14181             };
14182             if(this.useAjax){
14183                 Roo.applyIf(o, this.conn);
14184                 if(this.activeRequest){
14185                     Roo.Ajax.abort(this.activeRequest);
14186                 }
14187                 this.activeRequest = Roo.Ajax.request(o);
14188             }else{
14189                 this.conn.request(o);
14190             }
14191         }else{
14192             callback.call(scope||this, null, arg, false);
14193         }
14194     },
14195
14196     // private
14197     loadResponse : function(o, success, response){
14198         delete this.activeRequest;
14199         if(!success){
14200             this.fireEvent("loadexception", this, o, response);
14201             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14202             return;
14203         }
14204         var result;
14205         try {
14206             result = o.reader.read(response);
14207         }catch(e){
14208             this.fireEvent("loadexception", this, o, response, e);
14209             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14210             return;
14211         }
14212         
14213         this.fireEvent("load", this, o, o.request.arg);
14214         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14215     },
14216
14217     // private
14218     update : function(dataSet){
14219
14220     },
14221
14222     // private
14223     updateResponse : function(dataSet){
14224
14225     }
14226 });/*
14227  * Based on:
14228  * Ext JS Library 1.1.1
14229  * Copyright(c) 2006-2007, Ext JS, LLC.
14230  *
14231  * Originally Released Under LGPL - original licence link has changed is not relivant.
14232  *
14233  * Fork - LGPL
14234  * <script type="text/javascript">
14235  */
14236
14237 /**
14238  * @class Roo.data.ScriptTagProxy
14239  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14240  * other than the originating domain of the running page.<br><br>
14241  * <p>
14242  * <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
14243  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14244  * <p>
14245  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14246  * source code that is used as the source inside a &lt;script> tag.<br><br>
14247  * <p>
14248  * In order for the browser to process the returned data, the server must wrap the data object
14249  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14250  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14251  * depending on whether the callback name was passed:
14252  * <p>
14253  * <pre><code>
14254 boolean scriptTag = false;
14255 String cb = request.getParameter("callback");
14256 if (cb != null) {
14257     scriptTag = true;
14258     response.setContentType("text/javascript");
14259 } else {
14260     response.setContentType("application/x-json");
14261 }
14262 Writer out = response.getWriter();
14263 if (scriptTag) {
14264     out.write(cb + "(");
14265 }
14266 out.print(dataBlock.toJsonString());
14267 if (scriptTag) {
14268     out.write(");");
14269 }
14270 </pre></code>
14271  *
14272  * @constructor
14273  * @param {Object} config A configuration object.
14274  */
14275 Roo.data.ScriptTagProxy = function(config){
14276     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14277     Roo.apply(this, config);
14278     this.head = document.getElementsByTagName("head")[0];
14279 };
14280
14281 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14282
14283 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14284     /**
14285      * @cfg {String} url The URL from which to request the data object.
14286      */
14287     /**
14288      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14289      */
14290     timeout : 30000,
14291     /**
14292      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14293      * the server the name of the callback function set up by the load call to process the returned data object.
14294      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14295      * javascript output which calls this named function passing the data object as its only parameter.
14296      */
14297     callbackParam : "callback",
14298     /**
14299      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14300      * name to the request.
14301      */
14302     nocache : true,
14303
14304     /**
14305      * Load data from the configured URL, read the data object into
14306      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14307      * process that block using the passed callback.
14308      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14309      * for the request to the remote server.
14310      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14311      * object into a block of Roo.data.Records.
14312      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14313      * The function must be passed <ul>
14314      * <li>The Record block object</li>
14315      * <li>The "arg" argument from the load function</li>
14316      * <li>A boolean success indicator</li>
14317      * </ul>
14318      * @param {Object} scope The scope in which to call the callback
14319      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14320      */
14321     load : function(params, reader, callback, scope, arg){
14322         if(this.fireEvent("beforeload", this, params) !== false){
14323
14324             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14325
14326             var url = this.url;
14327             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14328             if(this.nocache){
14329                 url += "&_dc=" + (new Date().getTime());
14330             }
14331             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14332             var trans = {
14333                 id : transId,
14334                 cb : "stcCallback"+transId,
14335                 scriptId : "stcScript"+transId,
14336                 params : params,
14337                 arg : arg,
14338                 url : url,
14339                 callback : callback,
14340                 scope : scope,
14341                 reader : reader
14342             };
14343             var conn = this;
14344
14345             window[trans.cb] = function(o){
14346                 conn.handleResponse(o, trans);
14347             };
14348
14349             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14350
14351             if(this.autoAbort !== false){
14352                 this.abort();
14353             }
14354
14355             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14356
14357             var script = document.createElement("script");
14358             script.setAttribute("src", url);
14359             script.setAttribute("type", "text/javascript");
14360             script.setAttribute("id", trans.scriptId);
14361             this.head.appendChild(script);
14362
14363             this.trans = trans;
14364         }else{
14365             callback.call(scope||this, null, arg, false);
14366         }
14367     },
14368
14369     // private
14370     isLoading : function(){
14371         return this.trans ? true : false;
14372     },
14373
14374     /**
14375      * Abort the current server request.
14376      */
14377     abort : function(){
14378         if(this.isLoading()){
14379             this.destroyTrans(this.trans);
14380         }
14381     },
14382
14383     // private
14384     destroyTrans : function(trans, isLoaded){
14385         this.head.removeChild(document.getElementById(trans.scriptId));
14386         clearTimeout(trans.timeoutId);
14387         if(isLoaded){
14388             window[trans.cb] = undefined;
14389             try{
14390                 delete window[trans.cb];
14391             }catch(e){}
14392         }else{
14393             // if hasn't been loaded, wait for load to remove it to prevent script error
14394             window[trans.cb] = function(){
14395                 window[trans.cb] = undefined;
14396                 try{
14397                     delete window[trans.cb];
14398                 }catch(e){}
14399             };
14400         }
14401     },
14402
14403     // private
14404     handleResponse : function(o, trans){
14405         this.trans = false;
14406         this.destroyTrans(trans, true);
14407         var result;
14408         try {
14409             result = trans.reader.readRecords(o);
14410         }catch(e){
14411             this.fireEvent("loadexception", this, o, trans.arg, e);
14412             trans.callback.call(trans.scope||window, null, trans.arg, false);
14413             return;
14414         }
14415         this.fireEvent("load", this, o, trans.arg);
14416         trans.callback.call(trans.scope||window, result, trans.arg, true);
14417     },
14418
14419     // private
14420     handleFailure : function(trans){
14421         this.trans = false;
14422         this.destroyTrans(trans, false);
14423         this.fireEvent("loadexception", this, null, trans.arg);
14424         trans.callback.call(trans.scope||window, null, trans.arg, false);
14425     }
14426 });/*
14427  * Based on:
14428  * Ext JS Library 1.1.1
14429  * Copyright(c) 2006-2007, Ext JS, LLC.
14430  *
14431  * Originally Released Under LGPL - original licence link has changed is not relivant.
14432  *
14433  * Fork - LGPL
14434  * <script type="text/javascript">
14435  */
14436
14437 /**
14438  * @class Roo.data.JsonReader
14439  * @extends Roo.data.DataReader
14440  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14441  * based on mappings in a provided Roo.data.Record constructor.
14442  * 
14443  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14444  * in the reply previously. 
14445  * 
14446  * <p>
14447  * Example code:
14448  * <pre><code>
14449 var RecordDef = Roo.data.Record.create([
14450     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14451     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14452 ]);
14453 var myReader = new Roo.data.JsonReader({
14454     totalProperty: "results",    // The property which contains the total dataset size (optional)
14455     root: "rows",                // The property which contains an Array of row objects
14456     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14457 }, RecordDef);
14458 </code></pre>
14459  * <p>
14460  * This would consume a JSON file like this:
14461  * <pre><code>
14462 { 'results': 2, 'rows': [
14463     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14464     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14465 }
14466 </code></pre>
14467  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14468  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14469  * paged from the remote server.
14470  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14471  * @cfg {String} root name of the property which contains the Array of row objects.
14472  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14473  * @cfg {Array} fields Array of field definition objects
14474  * @constructor
14475  * Create a new JsonReader
14476  * @param {Object} meta Metadata configuration options
14477  * @param {Object} recordType Either an Array of field definition objects,
14478  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14479  */
14480 Roo.data.JsonReader = function(meta, recordType){
14481     
14482     meta = meta || {};
14483     // set some defaults:
14484     Roo.applyIf(meta, {
14485         totalProperty: 'total',
14486         successProperty : 'success',
14487         root : 'data',
14488         id : 'id'
14489     });
14490     
14491     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14492 };
14493 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14494     
14495     readerType : 'Json',
14496     
14497     /**
14498      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14499      * Used by Store query builder to append _requestMeta to params.
14500      * 
14501      */
14502     metaFromRemote : false,
14503     /**
14504      * This method is only used by a DataProxy which has retrieved data from a remote server.
14505      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14506      * @return {Object} data A data block which is used by an Roo.data.Store object as
14507      * a cache of Roo.data.Records.
14508      */
14509     read : function(response){
14510         var json = response.responseText;
14511        
14512         var o = /* eval:var:o */ eval("("+json+")");
14513         if(!o) {
14514             throw {message: "JsonReader.read: Json object not found"};
14515         }
14516         
14517         if(o.metaData){
14518             
14519             delete this.ef;
14520             this.metaFromRemote = true;
14521             this.meta = o.metaData;
14522             this.recordType = Roo.data.Record.create(o.metaData.fields);
14523             this.onMetaChange(this.meta, this.recordType, o);
14524         }
14525         return this.readRecords(o);
14526     },
14527
14528     // private function a store will implement
14529     onMetaChange : function(meta, recordType, o){
14530
14531     },
14532
14533     /**
14534          * @ignore
14535          */
14536     simpleAccess: function(obj, subsc) {
14537         return obj[subsc];
14538     },
14539
14540         /**
14541          * @ignore
14542          */
14543     getJsonAccessor: function(){
14544         var re = /[\[\.]/;
14545         return function(expr) {
14546             try {
14547                 return(re.test(expr))
14548                     ? new Function("obj", "return obj." + expr)
14549                     : function(obj){
14550                         return obj[expr];
14551                     };
14552             } catch(e){}
14553             return Roo.emptyFn;
14554         };
14555     }(),
14556
14557     /**
14558      * Create a data block containing Roo.data.Records from an XML document.
14559      * @param {Object} o An object which contains an Array of row objects in the property specified
14560      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14561      * which contains the total size of the dataset.
14562      * @return {Object} data A data block which is used by an Roo.data.Store object as
14563      * a cache of Roo.data.Records.
14564      */
14565     readRecords : function(o){
14566         /**
14567          * After any data loads, the raw JSON data is available for further custom processing.
14568          * @type Object
14569          */
14570         this.o = o;
14571         var s = this.meta, Record = this.recordType,
14572             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14573
14574 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14575         if (!this.ef) {
14576             if(s.totalProperty) {
14577                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14578                 }
14579                 if(s.successProperty) {
14580                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14581                 }
14582                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14583                 if (s.id) {
14584                         var g = this.getJsonAccessor(s.id);
14585                         this.getId = function(rec) {
14586                                 var r = g(rec);  
14587                                 return (r === undefined || r === "") ? null : r;
14588                         };
14589                 } else {
14590                         this.getId = function(){return null;};
14591                 }
14592             this.ef = [];
14593             for(var jj = 0; jj < fl; jj++){
14594                 f = fi[jj];
14595                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14596                 this.ef[jj] = this.getJsonAccessor(map);
14597             }
14598         }
14599
14600         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14601         if(s.totalProperty){
14602             var vt = parseInt(this.getTotal(o), 10);
14603             if(!isNaN(vt)){
14604                 totalRecords = vt;
14605             }
14606         }
14607         if(s.successProperty){
14608             var vs = this.getSuccess(o);
14609             if(vs === false || vs === 'false'){
14610                 success = false;
14611             }
14612         }
14613         var records = [];
14614         for(var i = 0; i < c; i++){
14615                 var n = root[i];
14616             var values = {};
14617             var id = this.getId(n);
14618             for(var j = 0; j < fl; j++){
14619                 f = fi[j];
14620             var v = this.ef[j](n);
14621             if (!f.convert) {
14622                 Roo.log('missing convert for ' + f.name);
14623                 Roo.log(f);
14624                 continue;
14625             }
14626             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14627             }
14628             var record = new Record(values, id);
14629             record.json = n;
14630             records[i] = record;
14631         }
14632         return {
14633             raw : o,
14634             success : success,
14635             records : records,
14636             totalRecords : totalRecords
14637         };
14638     },
14639     // used when loading children.. @see loadDataFromChildren
14640     toLoadData: function(rec)
14641     {
14642         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14643         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14644         return { data : data, total : data.length };
14645         
14646     }
14647 });/*
14648  * Based on:
14649  * Ext JS Library 1.1.1
14650  * Copyright(c) 2006-2007, Ext JS, LLC.
14651  *
14652  * Originally Released Under LGPL - original licence link has changed is not relivant.
14653  *
14654  * Fork - LGPL
14655  * <script type="text/javascript">
14656  */
14657
14658 /**
14659  * @class Roo.data.ArrayReader
14660  * @extends Roo.data.DataReader
14661  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14662  * Each element of that Array represents a row of data fields. The
14663  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14664  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14665  * <p>
14666  * Example code:.
14667  * <pre><code>
14668 var RecordDef = Roo.data.Record.create([
14669     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14670     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14671 ]);
14672 var myReader = new Roo.data.ArrayReader({
14673     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14674 }, RecordDef);
14675 </code></pre>
14676  * <p>
14677  * This would consume an Array like this:
14678  * <pre><code>
14679 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14680   </code></pre>
14681  
14682  * @constructor
14683  * Create a new JsonReader
14684  * @param {Object} meta Metadata configuration options.
14685  * @param {Object|Array} recordType Either an Array of field definition objects
14686  * 
14687  * @cfg {Array} fields Array of field definition objects
14688  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14689  * as specified to {@link Roo.data.Record#create},
14690  * or an {@link Roo.data.Record} object
14691  *
14692  * 
14693  * created using {@link Roo.data.Record#create}.
14694  */
14695 Roo.data.ArrayReader = function(meta, recordType)
14696 {    
14697     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14698 };
14699
14700 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14701     
14702       /**
14703      * Create a data block containing Roo.data.Records from an XML document.
14704      * @param {Object} o An Array of row objects which represents the dataset.
14705      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14706      * a cache of Roo.data.Records.
14707      */
14708     readRecords : function(o)
14709     {
14710         var sid = this.meta ? this.meta.id : null;
14711         var recordType = this.recordType, fields = recordType.prototype.fields;
14712         var records = [];
14713         var root = o;
14714         for(var i = 0; i < root.length; i++){
14715                 var n = root[i];
14716             var values = {};
14717             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14718             for(var j = 0, jlen = fields.length; j < jlen; j++){
14719                 var f = fields.items[j];
14720                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14721                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14722                 v = f.convert(v);
14723                 values[f.name] = v;
14724             }
14725             var record = new recordType(values, id);
14726             record.json = n;
14727             records[records.length] = record;
14728         }
14729         return {
14730             records : records,
14731             totalRecords : records.length
14732         };
14733     },
14734     // used when loading children.. @see loadDataFromChildren
14735     toLoadData: function(rec)
14736     {
14737         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14738         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14739         
14740     }
14741     
14742     
14743 });/*
14744  * - LGPL
14745  * * 
14746  */
14747
14748 /**
14749  * @class Roo.bootstrap.ComboBox
14750  * @extends Roo.bootstrap.TriggerField
14751  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14752  * @cfg {Boolean} append (true|false) default false
14753  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14754  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14755  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14756  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14757  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14758  * @cfg {Boolean} animate default true
14759  * @cfg {Boolean} emptyResultText only for touch device
14760  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14761  * @cfg {String} emptyTitle default ''
14762  * @cfg {Number} width fixed with? experimental
14763  * @constructor
14764  * Create a new ComboBox.
14765  * @param {Object} config Configuration options
14766  */
14767 Roo.bootstrap.ComboBox = function(config){
14768     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14769     this.addEvents({
14770         /**
14771          * @event expand
14772          * Fires when the dropdown list is expanded
14773         * @param {Roo.bootstrap.ComboBox} combo This combo box
14774         */
14775         'expand' : true,
14776         /**
14777          * @event collapse
14778          * Fires when the dropdown list is collapsed
14779         * @param {Roo.bootstrap.ComboBox} combo This combo box
14780         */
14781         'collapse' : true,
14782         /**
14783          * @event beforeselect
14784          * Fires before a list item is selected. Return false to cancel the selection.
14785         * @param {Roo.bootstrap.ComboBox} combo This combo box
14786         * @param {Roo.data.Record} record The data record returned from the underlying store
14787         * @param {Number} index The index of the selected item in the dropdown list
14788         */
14789         'beforeselect' : true,
14790         /**
14791          * @event select
14792          * Fires when a list item is selected
14793         * @param {Roo.bootstrap.ComboBox} combo This combo box
14794         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14795         * @param {Number} index The index of the selected item in the dropdown list
14796         */
14797         'select' : true,
14798         /**
14799          * @event beforequery
14800          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14801          * The event object passed has these properties:
14802         * @param {Roo.bootstrap.ComboBox} combo This combo box
14803         * @param {String} query The query
14804         * @param {Boolean} forceAll true to force "all" query
14805         * @param {Boolean} cancel true to cancel the query
14806         * @param {Object} e The query event object
14807         */
14808         'beforequery': true,
14809          /**
14810          * @event add
14811          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14812         * @param {Roo.bootstrap.ComboBox} combo This combo box
14813         */
14814         'add' : true,
14815         /**
14816          * @event edit
14817          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14818         * @param {Roo.bootstrap.ComboBox} combo This combo box
14819         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14820         */
14821         'edit' : true,
14822         /**
14823          * @event remove
14824          * Fires when the remove value from the combobox array
14825         * @param {Roo.bootstrap.ComboBox} combo This combo box
14826         */
14827         'remove' : true,
14828         /**
14829          * @event afterremove
14830          * Fires when the remove value from the combobox array
14831         * @param {Roo.bootstrap.ComboBox} combo This combo box
14832         */
14833         'afterremove' : true,
14834         /**
14835          * @event specialfilter
14836          * Fires when specialfilter
14837             * @param {Roo.bootstrap.ComboBox} combo This combo box
14838             */
14839         'specialfilter' : true,
14840         /**
14841          * @event tick
14842          * Fires when tick the element
14843             * @param {Roo.bootstrap.ComboBox} combo This combo box
14844             */
14845         'tick' : true,
14846         /**
14847          * @event touchviewdisplay
14848          * Fires when touch view require special display (default is using displayField)
14849             * @param {Roo.bootstrap.ComboBox} combo This combo box
14850             * @param {Object} cfg set html .
14851             */
14852         'touchviewdisplay' : true
14853         
14854     });
14855     
14856     this.item = [];
14857     this.tickItems = [];
14858     
14859     this.selectedIndex = -1;
14860     if(this.mode == 'local'){
14861         if(config.queryDelay === undefined){
14862             this.queryDelay = 10;
14863         }
14864         if(config.minChars === undefined){
14865             this.minChars = 0;
14866         }
14867     }
14868 };
14869
14870 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14871      
14872     /**
14873      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14874      * rendering into an Roo.Editor, defaults to false)
14875      */
14876     /**
14877      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14878      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14879      */
14880     /**
14881      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14882      */
14883     /**
14884      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14885      * the dropdown list (defaults to undefined, with no header element)
14886      */
14887
14888      /**
14889      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
14890      */
14891      
14892      /**
14893      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14894      */
14895     listWidth: undefined,
14896     /**
14897      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14898      * mode = 'remote' or 'text' if mode = 'local')
14899      */
14900     displayField: undefined,
14901     
14902     /**
14903      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14904      * mode = 'remote' or 'value' if mode = 'local'). 
14905      * Note: use of a valueField requires the user make a selection
14906      * in order for a value to be mapped.
14907      */
14908     valueField: undefined,
14909     /**
14910      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14911      */
14912     modalTitle : '',
14913     
14914     /**
14915      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14916      * field's data value (defaults to the underlying DOM element's name)
14917      */
14918     hiddenName: undefined,
14919     /**
14920      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14921      */
14922     listClass: '',
14923     /**
14924      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14925      */
14926     selectedClass: 'active',
14927     
14928     /**
14929      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14930      */
14931     shadow:'sides',
14932     /**
14933      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14934      * anchor positions (defaults to 'tl-bl')
14935      */
14936     listAlign: 'tl-bl?',
14937     /**
14938      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14939      */
14940     maxHeight: 300,
14941     /**
14942      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14943      * query specified by the allQuery config option (defaults to 'query')
14944      */
14945     triggerAction: 'query',
14946     /**
14947      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14948      * (defaults to 4, does not apply if editable = false)
14949      */
14950     minChars : 4,
14951     /**
14952      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14953      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14954      */
14955     typeAhead: false,
14956     /**
14957      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14958      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14959      */
14960     queryDelay: 500,
14961     /**
14962      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14963      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14964      */
14965     pageSize: 0,
14966     /**
14967      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14968      * when editable = true (defaults to false)
14969      */
14970     selectOnFocus:false,
14971     /**
14972      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14973      */
14974     queryParam: 'query',
14975     /**
14976      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14977      * when mode = 'remote' (defaults to 'Loading...')
14978      */
14979     loadingText: 'Loading...',
14980     /**
14981      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14982      */
14983     resizable: false,
14984     /**
14985      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14986      */
14987     handleHeight : 8,
14988     /**
14989      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14990      * traditional select (defaults to true)
14991      */
14992     editable: true,
14993     /**
14994      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14995      */
14996     allQuery: '',
14997     /**
14998      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14999      */
15000     mode: 'remote',
15001     /**
15002      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15003      * listWidth has a higher value)
15004      */
15005     minListWidth : 70,
15006     /**
15007      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15008      * allow the user to set arbitrary text into the field (defaults to false)
15009      */
15010     forceSelection:false,
15011     /**
15012      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15013      * if typeAhead = true (defaults to 250)
15014      */
15015     typeAheadDelay : 250,
15016     /**
15017      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15018      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15019      */
15020     valueNotFoundText : undefined,
15021     /**
15022      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15023      */
15024     blockFocus : false,
15025     
15026     /**
15027      * @cfg {Boolean} disableClear Disable showing of clear button.
15028      */
15029     disableClear : false,
15030     /**
15031      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15032      */
15033     alwaysQuery : false,
15034     
15035     /**
15036      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15037      */
15038     multiple : false,
15039     
15040     /**
15041      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15042      */
15043     invalidClass : "has-warning",
15044     
15045     /**
15046      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15047      */
15048     validClass : "has-success",
15049     
15050     /**
15051      * @cfg {Boolean} specialFilter (true|false) special filter default false
15052      */
15053     specialFilter : false,
15054     
15055     /**
15056      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15057      */
15058     mobileTouchView : true,
15059     
15060     /**
15061      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15062      */
15063     useNativeIOS : false,
15064     
15065     /**
15066      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15067      */
15068     mobile_restrict_height : false,
15069     
15070     ios_options : false,
15071     
15072     //private
15073     addicon : false,
15074     editicon: false,
15075     
15076     page: 0,
15077     hasQuery: false,
15078     append: false,
15079     loadNext: false,
15080     autoFocus : true,
15081     tickable : false,
15082     btnPosition : 'right',
15083     triggerList : true,
15084     showToggleBtn : true,
15085     animate : true,
15086     emptyResultText: 'Empty',
15087     triggerText : 'Select',
15088     emptyTitle : '',
15089     width : false,
15090     
15091     // element that contains real text value.. (when hidden is used..)
15092     
15093     getAutoCreate : function()
15094     {   
15095         var cfg = false;
15096         //render
15097         /*
15098          * Render classic select for iso
15099          */
15100         
15101         if(Roo.isIOS && this.useNativeIOS){
15102             cfg = this.getAutoCreateNativeIOS();
15103             return cfg;
15104         }
15105         
15106         /*
15107          * Touch Devices
15108          */
15109         
15110         if(Roo.isTouch && this.mobileTouchView){
15111             cfg = this.getAutoCreateTouchView();
15112             return cfg;;
15113         }
15114         
15115         /*
15116          *  Normal ComboBox
15117          */
15118         if(!this.tickable){
15119             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15120             return cfg;
15121         }
15122         
15123         /*
15124          *  ComboBox with tickable selections
15125          */
15126              
15127         var align = this.labelAlign || this.parentLabelAlign();
15128         
15129         cfg = {
15130             cls : 'form-group roo-combobox-tickable' //input-group
15131         };
15132         
15133         var btn_text_select = '';
15134         var btn_text_done = '';
15135         var btn_text_cancel = '';
15136         
15137         if (this.btn_text_show) {
15138             btn_text_select = 'Select';
15139             btn_text_done = 'Done';
15140             btn_text_cancel = 'Cancel'; 
15141         }
15142         
15143         var buttons = {
15144             tag : 'div',
15145             cls : 'tickable-buttons',
15146             cn : [
15147                 {
15148                     tag : 'button',
15149                     type : 'button',
15150                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15151                     //html : this.triggerText
15152                     html: btn_text_select
15153                 },
15154                 {
15155                     tag : 'button',
15156                     type : 'button',
15157                     name : 'ok',
15158                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15159                     //html : 'Done'
15160                     html: btn_text_done
15161                 },
15162                 {
15163                     tag : 'button',
15164                     type : 'button',
15165                     name : 'cancel',
15166                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15167                     //html : 'Cancel'
15168                     html: btn_text_cancel
15169                 }
15170             ]
15171         };
15172         
15173         if(this.editable){
15174             buttons.cn.unshift({
15175                 tag: 'input',
15176                 cls: 'roo-select2-search-field-input'
15177             });
15178         }
15179         
15180         var _this = this;
15181         
15182         Roo.each(buttons.cn, function(c){
15183             if (_this.size) {
15184                 c.cls += ' btn-' + _this.size;
15185             }
15186
15187             if (_this.disabled) {
15188                 c.disabled = true;
15189             }
15190         });
15191         
15192         var box = {
15193             tag: 'div',
15194             style : 'display: contents',
15195             cn: [
15196                 {
15197                     tag: 'input',
15198                     type : 'hidden',
15199                     cls: 'form-hidden-field'
15200                 },
15201                 {
15202                     tag: 'ul',
15203                     cls: 'roo-select2-choices',
15204                     cn:[
15205                         {
15206                             tag: 'li',
15207                             cls: 'roo-select2-search-field',
15208                             cn: [
15209                                 buttons
15210                             ]
15211                         }
15212                     ]
15213                 }
15214             ]
15215         };
15216         
15217         var combobox = {
15218             cls: 'roo-select2-container input-group roo-select2-container-multi',
15219             cn: [
15220                 
15221                 box
15222 //                {
15223 //                    tag: 'ul',
15224 //                    cls: 'typeahead typeahead-long dropdown-menu',
15225 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15226 //                }
15227             ]
15228         };
15229         
15230         if(this.hasFeedback && !this.allowBlank){
15231             
15232             var feedback = {
15233                 tag: 'span',
15234                 cls: 'glyphicon form-control-feedback'
15235             };
15236
15237             combobox.cn.push(feedback);
15238         }
15239         
15240         
15241         
15242         var indicator = {
15243             tag : 'i',
15244             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15245             tooltip : 'This field is required'
15246         };
15247         if (Roo.bootstrap.version == 4) {
15248             indicator = {
15249                 tag : 'i',
15250                 style : 'display:none'
15251             };
15252         }
15253         if (align ==='left' && this.fieldLabel.length) {
15254             
15255             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15256             
15257             cfg.cn = [
15258                 indicator,
15259                 {
15260                     tag: 'label',
15261                     'for' :  id,
15262                     cls : 'control-label col-form-label',
15263                     html : this.fieldLabel
15264
15265                 },
15266                 {
15267                     cls : "", 
15268                     cn: [
15269                         combobox
15270                     ]
15271                 }
15272
15273             ];
15274             
15275             var labelCfg = cfg.cn[1];
15276             var contentCfg = cfg.cn[2];
15277             
15278
15279             if(this.indicatorpos == 'right'){
15280                 
15281                 cfg.cn = [
15282                     {
15283                         tag: 'label',
15284                         'for' :  id,
15285                         cls : 'control-label col-form-label',
15286                         cn : [
15287                             {
15288                                 tag : 'span',
15289                                 html : this.fieldLabel
15290                             },
15291                             indicator
15292                         ]
15293                     },
15294                     {
15295                         cls : "",
15296                         cn: [
15297                             combobox
15298                         ]
15299                     }
15300
15301                 ];
15302                 
15303                 
15304                 
15305                 labelCfg = cfg.cn[0];
15306                 contentCfg = cfg.cn[1];
15307             
15308             }
15309             
15310             if(this.labelWidth > 12){
15311                 labelCfg.style = "width: " + this.labelWidth + 'px';
15312             }
15313             if(this.width * 1 > 0){
15314                 contentCfg.style = "width: " + this.width + 'px';
15315             }
15316             if(this.labelWidth < 13 && this.labelmd == 0){
15317                 this.labelmd = this.labelWidth;
15318             }
15319             
15320             if(this.labellg > 0){
15321                 labelCfg.cls += ' col-lg-' + this.labellg;
15322                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15323             }
15324             
15325             if(this.labelmd > 0){
15326                 labelCfg.cls += ' col-md-' + this.labelmd;
15327                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15328             }
15329             
15330             if(this.labelsm > 0){
15331                 labelCfg.cls += ' col-sm-' + this.labelsm;
15332                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15333             }
15334             
15335             if(this.labelxs > 0){
15336                 labelCfg.cls += ' col-xs-' + this.labelxs;
15337                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15338             }
15339                 
15340                 
15341         } else if ( this.fieldLabel.length) {
15342 //                Roo.log(" label");
15343                  cfg.cn = [
15344                    indicator,
15345                     {
15346                         tag: 'label',
15347                         //cls : 'input-group-addon',
15348                         html : this.fieldLabel
15349                     },
15350                     combobox
15351                 ];
15352                 
15353                 if(this.indicatorpos == 'right'){
15354                     cfg.cn = [
15355                         {
15356                             tag: 'label',
15357                             //cls : 'input-group-addon',
15358                             html : this.fieldLabel
15359                         },
15360                         indicator,
15361                         combobox
15362                     ];
15363                     
15364                 }
15365
15366         } else {
15367             
15368 //                Roo.log(" no label && no align");
15369                 cfg = combobox
15370                      
15371                 
15372         }
15373          
15374         var settings=this;
15375         ['xs','sm','md','lg'].map(function(size){
15376             if (settings[size]) {
15377                 cfg.cls += ' col-' + size + '-' + settings[size];
15378             }
15379         });
15380         
15381         return cfg;
15382         
15383     },
15384     
15385     _initEventsCalled : false,
15386     
15387     // private
15388     initEvents: function()
15389     {   
15390         if (this._initEventsCalled) { // as we call render... prevent looping...
15391             return;
15392         }
15393         this._initEventsCalled = true;
15394         
15395         if (!this.store) {
15396             throw "can not find store for combo";
15397         }
15398         
15399         this.indicator = this.indicatorEl();
15400         
15401         this.store = Roo.factory(this.store, Roo.data);
15402         this.store.parent = this;
15403         
15404         // if we are building from html. then this element is so complex, that we can not really
15405         // use the rendered HTML.
15406         // so we have to trash and replace the previous code.
15407         if (Roo.XComponent.build_from_html) {
15408             // remove this element....
15409             var e = this.el.dom, k=0;
15410             while (e ) { e = e.previousSibling;  ++k;}
15411
15412             this.el.remove();
15413             
15414             this.el=false;
15415             this.rendered = false;
15416             
15417             this.render(this.parent().getChildContainer(true), k);
15418         }
15419         
15420         if(Roo.isIOS && this.useNativeIOS){
15421             this.initIOSView();
15422             return;
15423         }
15424         
15425         /*
15426          * Touch Devices
15427          */
15428         
15429         if(Roo.isTouch && this.mobileTouchView){
15430             this.initTouchView();
15431             return;
15432         }
15433         
15434         if(this.tickable){
15435             this.initTickableEvents();
15436             return;
15437         }
15438         
15439         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15440         
15441         if(this.hiddenName){
15442             
15443             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15444             
15445             this.hiddenField.dom.value =
15446                 this.hiddenValue !== undefined ? this.hiddenValue :
15447                 this.value !== undefined ? this.value : '';
15448
15449             // prevent input submission
15450             this.el.dom.removeAttribute('name');
15451             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15452              
15453              
15454         }
15455         //if(Roo.isGecko){
15456         //    this.el.dom.setAttribute('autocomplete', 'off');
15457         //}
15458         
15459         var cls = 'x-combo-list';
15460         
15461         //this.list = new Roo.Layer({
15462         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15463         //});
15464         
15465         var _this = this;
15466         
15467         (function(){
15468             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15469             _this.list.setWidth(lw);
15470         }).defer(100);
15471         
15472         this.list.on('mouseover', this.onViewOver, this);
15473         this.list.on('mousemove', this.onViewMove, this);
15474         this.list.on('scroll', this.onViewScroll, this);
15475         
15476         /*
15477         this.list.swallowEvent('mousewheel');
15478         this.assetHeight = 0;
15479
15480         if(this.title){
15481             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15482             this.assetHeight += this.header.getHeight();
15483         }
15484
15485         this.innerList = this.list.createChild({cls:cls+'-inner'});
15486         this.innerList.on('mouseover', this.onViewOver, this);
15487         this.innerList.on('mousemove', this.onViewMove, this);
15488         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15489         
15490         if(this.allowBlank && !this.pageSize && !this.disableClear){
15491             this.footer = this.list.createChild({cls:cls+'-ft'});
15492             this.pageTb = new Roo.Toolbar(this.footer);
15493            
15494         }
15495         if(this.pageSize){
15496             this.footer = this.list.createChild({cls:cls+'-ft'});
15497             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15498                     {pageSize: this.pageSize});
15499             
15500         }
15501         
15502         if (this.pageTb && this.allowBlank && !this.disableClear) {
15503             var _this = this;
15504             this.pageTb.add(new Roo.Toolbar.Fill(), {
15505                 cls: 'x-btn-icon x-btn-clear',
15506                 text: '&#160;',
15507                 handler: function()
15508                 {
15509                     _this.collapse();
15510                     _this.clearValue();
15511                     _this.onSelect(false, -1);
15512                 }
15513             });
15514         }
15515         if (this.footer) {
15516             this.assetHeight += this.footer.getHeight();
15517         }
15518         */
15519             
15520         if(!this.tpl){
15521             this.tpl = Roo.bootstrap.version == 4 ?
15522                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15523                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15524         }
15525
15526         this.view = new Roo.View(this.list, this.tpl, {
15527             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15528         });
15529         //this.view.wrapEl.setDisplayed(false);
15530         this.view.on('click', this.onViewClick, this);
15531         
15532         
15533         this.store.on('beforeload', this.onBeforeLoad, this);
15534         this.store.on('load', this.onLoad, this);
15535         this.store.on('loadexception', this.onLoadException, this);
15536         /*
15537         if(this.resizable){
15538             this.resizer = new Roo.Resizable(this.list,  {
15539                pinned:true, handles:'se'
15540             });
15541             this.resizer.on('resize', function(r, w, h){
15542                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15543                 this.listWidth = w;
15544                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15545                 this.restrictHeight();
15546             }, this);
15547             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15548         }
15549         */
15550         if(!this.editable){
15551             this.editable = true;
15552             this.setEditable(false);
15553         }
15554         
15555         /*
15556         
15557         if (typeof(this.events.add.listeners) != 'undefined') {
15558             
15559             this.addicon = this.wrap.createChild(
15560                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15561        
15562             this.addicon.on('click', function(e) {
15563                 this.fireEvent('add', this);
15564             }, this);
15565         }
15566         if (typeof(this.events.edit.listeners) != 'undefined') {
15567             
15568             this.editicon = this.wrap.createChild(
15569                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15570             if (this.addicon) {
15571                 this.editicon.setStyle('margin-left', '40px');
15572             }
15573             this.editicon.on('click', function(e) {
15574                 
15575                 // we fire even  if inothing is selected..
15576                 this.fireEvent('edit', this, this.lastData );
15577                 
15578             }, this);
15579         }
15580         */
15581         
15582         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15583             "up" : function(e){
15584                 this.inKeyMode = true;
15585                 this.selectPrev();
15586             },
15587
15588             "down" : function(e){
15589                 if(!this.isExpanded()){
15590                     this.onTriggerClick();
15591                 }else{
15592                     this.inKeyMode = true;
15593                     this.selectNext();
15594                 }
15595             },
15596
15597             "enter" : function(e){
15598 //                this.onViewClick();
15599                 //return true;
15600                 this.collapse();
15601                 
15602                 if(this.fireEvent("specialkey", this, e)){
15603                     this.onViewClick(false);
15604                 }
15605                 
15606                 return true;
15607             },
15608
15609             "esc" : function(e){
15610                 this.collapse();
15611             },
15612
15613             "tab" : function(e){
15614                 this.collapse();
15615                 
15616                 if(this.fireEvent("specialkey", this, e)){
15617                     this.onViewClick(false);
15618                 }
15619                 
15620                 return true;
15621             },
15622
15623             scope : this,
15624
15625             doRelay : function(foo, bar, hname){
15626                 if(hname == 'down' || this.scope.isExpanded()){
15627                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15628                 }
15629                 return true;
15630             },
15631
15632             forceKeyDown: true
15633         });
15634         
15635         
15636         this.queryDelay = Math.max(this.queryDelay || 10,
15637                 this.mode == 'local' ? 10 : 250);
15638         
15639         
15640         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15641         
15642         if(this.typeAhead){
15643             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15644         }
15645         if(this.editable !== false){
15646             this.inputEl().on("keyup", this.onKeyUp, this);
15647         }
15648         if(this.forceSelection){
15649             this.inputEl().on('blur', this.doForce, this);
15650         }
15651         
15652         if(this.multiple){
15653             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15654             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15655         }
15656     },
15657     
15658     initTickableEvents: function()
15659     {   
15660         this.createList();
15661         
15662         if(this.hiddenName){
15663             
15664             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15665             
15666             this.hiddenField.dom.value =
15667                 this.hiddenValue !== undefined ? this.hiddenValue :
15668                 this.value !== undefined ? this.value : '';
15669
15670             // prevent input submission
15671             this.el.dom.removeAttribute('name');
15672             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15673              
15674              
15675         }
15676         
15677 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15678         
15679         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15680         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15681         if(this.triggerList){
15682             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15683         }
15684          
15685         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15686         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15687         
15688         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15689         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15690         
15691         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15692         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15693         
15694         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15695         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15696         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15697         
15698         this.okBtn.hide();
15699         this.cancelBtn.hide();
15700         
15701         var _this = this;
15702         
15703         (function(){
15704             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15705             _this.list.setWidth(lw);
15706         }).defer(100);
15707         
15708         this.list.on('mouseover', this.onViewOver, this);
15709         this.list.on('mousemove', this.onViewMove, this);
15710         
15711         this.list.on('scroll', this.onViewScroll, this);
15712         
15713         if(!this.tpl){
15714             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15715                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15716         }
15717
15718         this.view = new Roo.View(this.list, this.tpl, {
15719             singleSelect:true,
15720             tickable:true,
15721             parent:this,
15722             store: this.store,
15723             selectedClass: this.selectedClass
15724         });
15725         
15726         //this.view.wrapEl.setDisplayed(false);
15727         this.view.on('click', this.onViewClick, this);
15728         
15729         
15730         
15731         this.store.on('beforeload', this.onBeforeLoad, this);
15732         this.store.on('load', this.onLoad, this);
15733         this.store.on('loadexception', this.onLoadException, this);
15734         
15735         if(this.editable){
15736             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15737                 "up" : function(e){
15738                     this.inKeyMode = true;
15739                     this.selectPrev();
15740                 },
15741
15742                 "down" : function(e){
15743                     this.inKeyMode = true;
15744                     this.selectNext();
15745                 },
15746
15747                 "enter" : function(e){
15748                     if(this.fireEvent("specialkey", this, e)){
15749                         this.onViewClick(false);
15750                     }
15751                     
15752                     return true;
15753                 },
15754
15755                 "esc" : function(e){
15756                     this.onTickableFooterButtonClick(e, false, false);
15757                 },
15758
15759                 "tab" : function(e){
15760                     this.fireEvent("specialkey", this, e);
15761                     
15762                     this.onTickableFooterButtonClick(e, false, false);
15763                     
15764                     return true;
15765                 },
15766
15767                 scope : this,
15768
15769                 doRelay : function(e, fn, key){
15770                     if(this.scope.isExpanded()){
15771                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15772                     }
15773                     return true;
15774                 },
15775
15776                 forceKeyDown: true
15777             });
15778         }
15779         
15780         this.queryDelay = Math.max(this.queryDelay || 10,
15781                 this.mode == 'local' ? 10 : 250);
15782         
15783         
15784         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15785         
15786         if(this.typeAhead){
15787             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15788         }
15789         
15790         if(this.editable !== false){
15791             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15792         }
15793         
15794         this.indicator = this.indicatorEl();
15795         
15796         if(this.indicator){
15797             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15798             this.indicator.hide();
15799         }
15800         
15801     },
15802
15803     onDestroy : function(){
15804         if(this.view){
15805             this.view.setStore(null);
15806             this.view.el.removeAllListeners();
15807             this.view.el.remove();
15808             this.view.purgeListeners();
15809         }
15810         if(this.list){
15811             this.list.dom.innerHTML  = '';
15812         }
15813         
15814         if(this.store){
15815             this.store.un('beforeload', this.onBeforeLoad, this);
15816             this.store.un('load', this.onLoad, this);
15817             this.store.un('loadexception', this.onLoadException, this);
15818         }
15819         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15820     },
15821
15822     // private
15823     fireKey : function(e){
15824         if(e.isNavKeyPress() && !this.list.isVisible()){
15825             this.fireEvent("specialkey", this, e);
15826         }
15827     },
15828
15829     // private
15830     onResize: function(w, h)
15831     {
15832         
15833         
15834 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15835 //        
15836 //        if(typeof w != 'number'){
15837 //            // we do not handle it!?!?
15838 //            return;
15839 //        }
15840 //        var tw = this.trigger.getWidth();
15841 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15842 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15843 //        var x = w - tw;
15844 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15845 //            
15846 //        //this.trigger.setStyle('left', x+'px');
15847 //        
15848 //        if(this.list && this.listWidth === undefined){
15849 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15850 //            this.list.setWidth(lw);
15851 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15852 //        }
15853         
15854     
15855         
15856     },
15857
15858     /**
15859      * Allow or prevent the user from directly editing the field text.  If false is passed,
15860      * the user will only be able to select from the items defined in the dropdown list.  This method
15861      * is the runtime equivalent of setting the 'editable' config option at config time.
15862      * @param {Boolean} value True to allow the user to directly edit the field text
15863      */
15864     setEditable : function(value){
15865         if(value == this.editable){
15866             return;
15867         }
15868         this.editable = value;
15869         if(!value){
15870             this.inputEl().dom.setAttribute('readOnly', true);
15871             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15872             this.inputEl().addClass('x-combo-noedit');
15873         }else{
15874             this.inputEl().dom.setAttribute('readOnly', false);
15875             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15876             this.inputEl().removeClass('x-combo-noedit');
15877         }
15878     },
15879
15880     // private
15881     
15882     onBeforeLoad : function(combo,opts){
15883         if(!this.hasFocus){
15884             return;
15885         }
15886          if (!opts.add) {
15887             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15888          }
15889         this.restrictHeight();
15890         this.selectedIndex = -1;
15891     },
15892
15893     // private
15894     onLoad : function(){
15895         
15896         this.hasQuery = false;
15897         
15898         if(!this.hasFocus){
15899             return;
15900         }
15901         
15902         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15903             this.loading.hide();
15904         }
15905         
15906         if(this.store.getCount() > 0){
15907             
15908             this.expand();
15909             this.restrictHeight();
15910             if(this.lastQuery == this.allQuery){
15911                 if(this.editable && !this.tickable){
15912                     this.inputEl().dom.select();
15913                 }
15914                 
15915                 if(
15916                     !this.selectByValue(this.value, true) &&
15917                     this.autoFocus && 
15918                     (
15919                         !this.store.lastOptions ||
15920                         typeof(this.store.lastOptions.add) == 'undefined' || 
15921                         this.store.lastOptions.add != true
15922                     )
15923                 ){
15924                     this.select(0, true);
15925                 }
15926             }else{
15927                 if(this.autoFocus){
15928                     this.selectNext();
15929                 }
15930                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15931                     this.taTask.delay(this.typeAheadDelay);
15932                 }
15933             }
15934         }else{
15935             this.onEmptyResults();
15936         }
15937         
15938         //this.el.focus();
15939     },
15940     // private
15941     onLoadException : function()
15942     {
15943         this.hasQuery = false;
15944         
15945         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15946             this.loading.hide();
15947         }
15948         
15949         if(this.tickable && this.editable){
15950             return;
15951         }
15952         
15953         this.collapse();
15954         // only causes errors at present
15955         //Roo.log(this.store.reader.jsonData);
15956         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15957             // fixme
15958             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15959         //}
15960         
15961         
15962     },
15963     // private
15964     onTypeAhead : function(){
15965         if(this.store.getCount() > 0){
15966             var r = this.store.getAt(0);
15967             var newValue = r.data[this.displayField];
15968             var len = newValue.length;
15969             var selStart = this.getRawValue().length;
15970             
15971             if(selStart != len){
15972                 this.setRawValue(newValue);
15973                 this.selectText(selStart, newValue.length);
15974             }
15975         }
15976     },
15977
15978     // private
15979     onSelect : function(record, index){
15980         
15981         if(this.fireEvent('beforeselect', this, record, index) !== false){
15982         
15983             this.setFromData(index > -1 ? record.data : false);
15984             
15985             this.collapse();
15986             this.fireEvent('select', this, record, index);
15987         }
15988     },
15989
15990     /**
15991      * Returns the currently selected field value or empty string if no value is set.
15992      * @return {String} value The selected value
15993      */
15994     getValue : function()
15995     {
15996         if(Roo.isIOS && this.useNativeIOS){
15997             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15998         }
15999         
16000         if(this.multiple){
16001             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16002         }
16003         
16004         if(this.valueField){
16005             return typeof this.value != 'undefined' ? this.value : '';
16006         }else{
16007             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16008         }
16009     },
16010     
16011     getRawValue : function()
16012     {
16013         if(Roo.isIOS && this.useNativeIOS){
16014             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16015         }
16016         
16017         var v = this.inputEl().getValue();
16018         
16019         return v;
16020     },
16021
16022     /**
16023      * Clears any text/value currently set in the field
16024      */
16025     clearValue : function(){
16026         
16027         if(this.hiddenField){
16028             this.hiddenField.dom.value = '';
16029         }
16030         this.value = '';
16031         this.setRawValue('');
16032         this.lastSelectionText = '';
16033         this.lastData = false;
16034         
16035         var close = this.closeTriggerEl();
16036         
16037         if(close){
16038             close.hide();
16039         }
16040         
16041         this.validate();
16042         
16043     },
16044
16045     /**
16046      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16047      * will be displayed in the field.  If the value does not match the data value of an existing item,
16048      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16049      * Otherwise the field will be blank (although the value will still be set).
16050      * @param {String} value The value to match
16051      */
16052     setValue : function(v)
16053     {
16054         if(Roo.isIOS && this.useNativeIOS){
16055             this.setIOSValue(v);
16056             return;
16057         }
16058         
16059         if(this.multiple){
16060             this.syncValue();
16061             return;
16062         }
16063         
16064         var text = v;
16065         if(this.valueField){
16066             var r = this.findRecord(this.valueField, v);
16067             if(r){
16068                 text = r.data[this.displayField];
16069             }else if(this.valueNotFoundText !== undefined){
16070                 text = this.valueNotFoundText;
16071             }
16072         }
16073         this.lastSelectionText = text;
16074         if(this.hiddenField){
16075             this.hiddenField.dom.value = v;
16076         }
16077         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16078         this.value = v;
16079         
16080         var close = this.closeTriggerEl();
16081         
16082         if(close){
16083             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16084         }
16085         
16086         this.validate();
16087     },
16088     /**
16089      * @property {Object} the last set data for the element
16090      */
16091     
16092     lastData : false,
16093     /**
16094      * Sets the value of the field based on a object which is related to the record format for the store.
16095      * @param {Object} value the value to set as. or false on reset?
16096      */
16097     setFromData : function(o){
16098         
16099         if(this.multiple){
16100             this.addItem(o);
16101             return;
16102         }
16103             
16104         var dv = ''; // display value
16105         var vv = ''; // value value..
16106         this.lastData = o;
16107         if (this.displayField) {
16108             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16109         } else {
16110             // this is an error condition!!!
16111             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16112         }
16113         
16114         if(this.valueField){
16115             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16116         }
16117         
16118         var close = this.closeTriggerEl();
16119         
16120         if(close){
16121             if(dv.length || vv * 1 > 0){
16122                 close.show() ;
16123                 this.blockFocus=true;
16124             } else {
16125                 close.hide();
16126             }             
16127         }
16128         
16129         if(this.hiddenField){
16130             this.hiddenField.dom.value = vv;
16131             
16132             this.lastSelectionText = dv;
16133             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16134             this.value = vv;
16135             return;
16136         }
16137         // no hidden field.. - we store the value in 'value', but still display
16138         // display field!!!!
16139         this.lastSelectionText = dv;
16140         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16141         this.value = vv;
16142         
16143         
16144         
16145     },
16146     // private
16147     reset : function(){
16148         // overridden so that last data is reset..
16149         
16150         if(this.multiple){
16151             this.clearItem();
16152             return;
16153         }
16154         
16155         this.setValue(this.originalValue);
16156         //this.clearInvalid();
16157         this.lastData = false;
16158         if (this.view) {
16159             this.view.clearSelections();
16160         }
16161         
16162         this.validate();
16163     },
16164     // private
16165     findRecord : function(prop, value){
16166         var record;
16167         if(this.store.getCount() > 0){
16168             this.store.each(function(r){
16169                 if(r.data[prop] == value){
16170                     record = r;
16171                     return false;
16172                 }
16173                 return true;
16174             });
16175         }
16176         return record;
16177     },
16178     
16179     getName: function()
16180     {
16181         // returns hidden if it's set..
16182         if (!this.rendered) {return ''};
16183         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16184         
16185     },
16186     // private
16187     onViewMove : function(e, t){
16188         this.inKeyMode = false;
16189     },
16190
16191     // private
16192     onViewOver : function(e, t){
16193         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16194             return;
16195         }
16196         var item = this.view.findItemFromChild(t);
16197         
16198         if(item){
16199             var index = this.view.indexOf(item);
16200             this.select(index, false);
16201         }
16202     },
16203
16204     // private
16205     onViewClick : function(view, doFocus, el, e)
16206     {
16207         var index = this.view.getSelectedIndexes()[0];
16208         
16209         var r = this.store.getAt(index);
16210         
16211         if(this.tickable){
16212             
16213             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16214                 return;
16215             }
16216             
16217             var rm = false;
16218             var _this = this;
16219             
16220             Roo.each(this.tickItems, function(v,k){
16221                 
16222                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16223                     Roo.log(v);
16224                     _this.tickItems.splice(k, 1);
16225                     
16226                     if(typeof(e) == 'undefined' && view == false){
16227                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16228                     }
16229                     
16230                     rm = true;
16231                     return;
16232                 }
16233             });
16234             
16235             if(rm){
16236                 return;
16237             }
16238             
16239             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16240                 this.tickItems.push(r.data);
16241             }
16242             
16243             if(typeof(e) == 'undefined' && view == false){
16244                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16245             }
16246                     
16247             return;
16248         }
16249         
16250         if(r){
16251             this.onSelect(r, index);
16252         }
16253         if(doFocus !== false && !this.blockFocus){
16254             this.inputEl().focus();
16255         }
16256     },
16257
16258     // private
16259     restrictHeight : function(){
16260         //this.innerList.dom.style.height = '';
16261         //var inner = this.innerList.dom;
16262         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16263         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16264         //this.list.beginUpdate();
16265         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16266         this.list.alignTo(this.inputEl(), this.listAlign);
16267         this.list.alignTo(this.inputEl(), this.listAlign);
16268         //this.list.endUpdate();
16269     },
16270
16271     // private
16272     onEmptyResults : function(){
16273         
16274         if(this.tickable && this.editable){
16275             this.hasFocus = false;
16276             this.restrictHeight();
16277             return;
16278         }
16279         
16280         this.collapse();
16281     },
16282
16283     /**
16284      * Returns true if the dropdown list is expanded, else false.
16285      */
16286     isExpanded : function(){
16287         return this.list.isVisible();
16288     },
16289
16290     /**
16291      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16292      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16293      * @param {String} value The data value of the item to select
16294      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16295      * selected item if it is not currently in view (defaults to true)
16296      * @return {Boolean} True if the value matched an item in the list, else false
16297      */
16298     selectByValue : function(v, scrollIntoView){
16299         if(v !== undefined && v !== null){
16300             var r = this.findRecord(this.valueField || this.displayField, v);
16301             if(r){
16302                 this.select(this.store.indexOf(r), scrollIntoView);
16303                 return true;
16304             }
16305         }
16306         return false;
16307     },
16308
16309     /**
16310      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16311      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16312      * @param {Number} index The zero-based index of the list item to select
16313      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16314      * selected item if it is not currently in view (defaults to true)
16315      */
16316     select : function(index, scrollIntoView){
16317         this.selectedIndex = index;
16318         this.view.select(index);
16319         if(scrollIntoView !== false){
16320             var el = this.view.getNode(index);
16321             /*
16322              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16323              */
16324             if(el){
16325                 this.list.scrollChildIntoView(el, false);
16326             }
16327         }
16328     },
16329
16330     // private
16331     selectNext : function(){
16332         var ct = this.store.getCount();
16333         if(ct > 0){
16334             if(this.selectedIndex == -1){
16335                 this.select(0);
16336             }else if(this.selectedIndex < ct-1){
16337                 this.select(this.selectedIndex+1);
16338             }
16339         }
16340     },
16341
16342     // private
16343     selectPrev : function(){
16344         var ct = this.store.getCount();
16345         if(ct > 0){
16346             if(this.selectedIndex == -1){
16347                 this.select(0);
16348             }else if(this.selectedIndex != 0){
16349                 this.select(this.selectedIndex-1);
16350             }
16351         }
16352     },
16353
16354     // private
16355     onKeyUp : function(e){
16356         if(this.editable !== false && !e.isSpecialKey()){
16357             this.lastKey = e.getKey();
16358             this.dqTask.delay(this.queryDelay);
16359         }
16360     },
16361
16362     // private
16363     validateBlur : function(){
16364         return !this.list || !this.list.isVisible();   
16365     },
16366
16367     // private
16368     initQuery : function(){
16369         
16370         var v = this.getRawValue();
16371         
16372         if(this.tickable && this.editable){
16373             v = this.tickableInputEl().getValue();
16374         }
16375         
16376         this.doQuery(v);
16377     },
16378
16379     // private
16380     doForce : function(){
16381         if(this.inputEl().dom.value.length > 0){
16382             this.inputEl().dom.value =
16383                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16384              
16385         }
16386     },
16387
16388     /**
16389      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16390      * query allowing the query action to be canceled if needed.
16391      * @param {String} query The SQL query to execute
16392      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16393      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16394      * saved in the current store (defaults to false)
16395      */
16396     doQuery : function(q, forceAll){
16397         
16398         if(q === undefined || q === null){
16399             q = '';
16400         }
16401         var qe = {
16402             query: q,
16403             forceAll: forceAll,
16404             combo: this,
16405             cancel:false
16406         };
16407         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16408             return false;
16409         }
16410         q = qe.query;
16411         
16412         forceAll = qe.forceAll;
16413         if(forceAll === true || (q.length >= this.minChars)){
16414             
16415             this.hasQuery = true;
16416             
16417             if(this.lastQuery != q || this.alwaysQuery){
16418                 this.lastQuery = q;
16419                 if(this.mode == 'local'){
16420                     this.selectedIndex = -1;
16421                     if(forceAll){
16422                         this.store.clearFilter();
16423                     }else{
16424                         
16425                         if(this.specialFilter){
16426                             this.fireEvent('specialfilter', this);
16427                             this.onLoad();
16428                             return;
16429                         }
16430                         
16431                         this.store.filter(this.displayField, q);
16432                     }
16433                     
16434                     this.store.fireEvent("datachanged", this.store);
16435                     
16436                     this.onLoad();
16437                     
16438                     
16439                 }else{
16440                     
16441                     this.store.baseParams[this.queryParam] = q;
16442                     
16443                     var options = {params : this.getParams(q)};
16444                     
16445                     if(this.loadNext){
16446                         options.add = true;
16447                         options.params.start = this.page * this.pageSize;
16448                     }
16449                     
16450                     this.store.load(options);
16451                     
16452                     /*
16453                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16454                      *  we should expand the list on onLoad
16455                      *  so command out it
16456                      */
16457 //                    this.expand();
16458                 }
16459             }else{
16460                 this.selectedIndex = -1;
16461                 this.onLoad();   
16462             }
16463         }
16464         
16465         this.loadNext = false;
16466     },
16467     
16468     // private
16469     getParams : function(q){
16470         var p = {};
16471         //p[this.queryParam] = q;
16472         
16473         if(this.pageSize){
16474             p.start = 0;
16475             p.limit = this.pageSize;
16476         }
16477         return p;
16478     },
16479
16480     /**
16481      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16482      */
16483     collapse : function(){
16484         if(!this.isExpanded()){
16485             return;
16486         }
16487         
16488         this.list.hide();
16489         
16490         this.hasFocus = false;
16491         
16492         if(this.tickable){
16493             this.okBtn.hide();
16494             this.cancelBtn.hide();
16495             this.trigger.show();
16496             
16497             if(this.editable){
16498                 this.tickableInputEl().dom.value = '';
16499                 this.tickableInputEl().blur();
16500             }
16501             
16502         }
16503         
16504         Roo.get(document).un('mousedown', this.collapseIf, this);
16505         Roo.get(document).un('mousewheel', this.collapseIf, this);
16506         if (!this.editable) {
16507             Roo.get(document).un('keydown', this.listKeyPress, this);
16508         }
16509         this.fireEvent('collapse', this);
16510         
16511         this.validate();
16512     },
16513
16514     // private
16515     collapseIf : function(e){
16516         var in_combo  = e.within(this.el);
16517         var in_list =  e.within(this.list);
16518         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16519         
16520         if (in_combo || in_list || is_list) {
16521             //e.stopPropagation();
16522             return;
16523         }
16524         
16525         if(this.tickable){
16526             this.onTickableFooterButtonClick(e, false, false);
16527         }
16528
16529         this.collapse();
16530         
16531     },
16532
16533     /**
16534      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16535      */
16536     expand : function(){
16537        
16538         if(this.isExpanded() || !this.hasFocus){
16539             return;
16540         }
16541         
16542         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16543         this.list.setWidth(lw);
16544         
16545         Roo.log('expand');
16546         
16547         this.list.show();
16548         
16549         this.restrictHeight();
16550         
16551         if(this.tickable){
16552             
16553             this.tickItems = Roo.apply([], this.item);
16554             
16555             this.okBtn.show();
16556             this.cancelBtn.show();
16557             this.trigger.hide();
16558             
16559             if(this.editable){
16560                 this.tickableInputEl().focus();
16561             }
16562             
16563         }
16564         
16565         Roo.get(document).on('mousedown', this.collapseIf, this);
16566         Roo.get(document).on('mousewheel', this.collapseIf, this);
16567         if (!this.editable) {
16568             Roo.get(document).on('keydown', this.listKeyPress, this);
16569         }
16570         
16571         this.fireEvent('expand', this);
16572     },
16573
16574     // private
16575     // Implements the default empty TriggerField.onTriggerClick function
16576     onTriggerClick : function(e)
16577     {
16578         Roo.log('trigger click');
16579         
16580         if(this.disabled || !this.triggerList){
16581             return;
16582         }
16583         
16584         this.page = 0;
16585         this.loadNext = false;
16586         
16587         if(this.isExpanded()){
16588             this.collapse();
16589             if (!this.blockFocus) {
16590                 this.inputEl().focus();
16591             }
16592             
16593         }else {
16594             this.hasFocus = true;
16595             if(this.triggerAction == 'all') {
16596                 this.doQuery(this.allQuery, true);
16597             } else {
16598                 this.doQuery(this.getRawValue());
16599             }
16600             if (!this.blockFocus) {
16601                 this.inputEl().focus();
16602             }
16603         }
16604     },
16605     
16606     onTickableTriggerClick : function(e)
16607     {
16608         if(this.disabled){
16609             return;
16610         }
16611         
16612         this.page = 0;
16613         this.loadNext = false;
16614         this.hasFocus = true;
16615         
16616         if(this.triggerAction == 'all') {
16617             this.doQuery(this.allQuery, true);
16618         } else {
16619             this.doQuery(this.getRawValue());
16620         }
16621     },
16622     
16623     onSearchFieldClick : function(e)
16624     {
16625         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16626             this.onTickableFooterButtonClick(e, false, false);
16627             return;
16628         }
16629         
16630         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16631             return;
16632         }
16633         
16634         this.page = 0;
16635         this.loadNext = false;
16636         this.hasFocus = true;
16637         
16638         if(this.triggerAction == 'all') {
16639             this.doQuery(this.allQuery, true);
16640         } else {
16641             this.doQuery(this.getRawValue());
16642         }
16643     },
16644     
16645     listKeyPress : function(e)
16646     {
16647         //Roo.log('listkeypress');
16648         // scroll to first matching element based on key pres..
16649         if (e.isSpecialKey()) {
16650             return false;
16651         }
16652         var k = String.fromCharCode(e.getKey()).toUpperCase();
16653         //Roo.log(k);
16654         var match  = false;
16655         var csel = this.view.getSelectedNodes();
16656         var cselitem = false;
16657         if (csel.length) {
16658             var ix = this.view.indexOf(csel[0]);
16659             cselitem  = this.store.getAt(ix);
16660             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16661                 cselitem = false;
16662             }
16663             
16664         }
16665         
16666         this.store.each(function(v) { 
16667             if (cselitem) {
16668                 // start at existing selection.
16669                 if (cselitem.id == v.id) {
16670                     cselitem = false;
16671                 }
16672                 return true;
16673             }
16674                 
16675             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16676                 match = this.store.indexOf(v);
16677                 return false;
16678             }
16679             return true;
16680         }, this);
16681         
16682         if (match === false) {
16683             return true; // no more action?
16684         }
16685         // scroll to?
16686         this.view.select(match);
16687         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16688         sn.scrollIntoView(sn.dom.parentNode, false);
16689     },
16690     
16691     onViewScroll : function(e, t){
16692         
16693         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){
16694             return;
16695         }
16696         
16697         this.hasQuery = true;
16698         
16699         this.loading = this.list.select('.loading', true).first();
16700         
16701         if(this.loading === null){
16702             this.list.createChild({
16703                 tag: 'div',
16704                 cls: 'loading roo-select2-more-results roo-select2-active',
16705                 html: 'Loading more results...'
16706             });
16707             
16708             this.loading = this.list.select('.loading', true).first();
16709             
16710             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16711             
16712             this.loading.hide();
16713         }
16714         
16715         this.loading.show();
16716         
16717         var _combo = this;
16718         
16719         this.page++;
16720         this.loadNext = true;
16721         
16722         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16723         
16724         return;
16725     },
16726     
16727     addItem : function(o)
16728     {   
16729         var dv = ''; // display value
16730         
16731         if (this.displayField) {
16732             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16733         } else {
16734             // this is an error condition!!!
16735             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16736         }
16737         
16738         if(!dv.length){
16739             return;
16740         }
16741         
16742         var choice = this.choices.createChild({
16743             tag: 'li',
16744             cls: 'roo-select2-search-choice',
16745             cn: [
16746                 {
16747                     tag: 'div',
16748                     html: dv
16749                 },
16750                 {
16751                     tag: 'a',
16752                     href: '#',
16753                     cls: 'roo-select2-search-choice-close fa fa-times',
16754                     tabindex: '-1'
16755                 }
16756             ]
16757             
16758         }, this.searchField);
16759         
16760         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16761         
16762         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16763         
16764         this.item.push(o);
16765         
16766         this.lastData = o;
16767         
16768         this.syncValue();
16769         
16770         this.inputEl().dom.value = '';
16771         
16772         this.validate();
16773     },
16774     
16775     onRemoveItem : function(e, _self, o)
16776     {
16777         e.preventDefault();
16778         
16779         this.lastItem = Roo.apply([], this.item);
16780         
16781         var index = this.item.indexOf(o.data) * 1;
16782         
16783         if( index < 0){
16784             Roo.log('not this item?!');
16785             return;
16786         }
16787         
16788         this.item.splice(index, 1);
16789         o.item.remove();
16790         
16791         this.syncValue();
16792         
16793         this.fireEvent('remove', this, e);
16794         
16795         this.validate();
16796         
16797     },
16798     
16799     syncValue : function()
16800     {
16801         if(!this.item.length){
16802             this.clearValue();
16803             return;
16804         }
16805             
16806         var value = [];
16807         var _this = this;
16808         Roo.each(this.item, function(i){
16809             if(_this.valueField){
16810                 value.push(i[_this.valueField]);
16811                 return;
16812             }
16813
16814             value.push(i);
16815         });
16816
16817         this.value = value.join(',');
16818
16819         if(this.hiddenField){
16820             this.hiddenField.dom.value = this.value;
16821         }
16822         
16823         this.store.fireEvent("datachanged", this.store);
16824         
16825         this.validate();
16826     },
16827     
16828     clearItem : function()
16829     {
16830         if(!this.multiple){
16831             return;
16832         }
16833         
16834         this.item = [];
16835         
16836         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16837            c.remove();
16838         });
16839         
16840         this.syncValue();
16841         
16842         this.validate();
16843         
16844         if(this.tickable && !Roo.isTouch){
16845             this.view.refresh();
16846         }
16847     },
16848     
16849     inputEl: function ()
16850     {
16851         if(Roo.isIOS && this.useNativeIOS){
16852             return this.el.select('select.roo-ios-select', true).first();
16853         }
16854         
16855         if(Roo.isTouch && this.mobileTouchView){
16856             return this.el.select('input.form-control',true).first();
16857         }
16858         
16859         if(this.tickable){
16860             return this.searchField;
16861         }
16862         
16863         return this.el.select('input.form-control',true).first();
16864     },
16865     
16866     onTickableFooterButtonClick : function(e, btn, el)
16867     {
16868         e.preventDefault();
16869         
16870         this.lastItem = Roo.apply([], this.item);
16871         
16872         if(btn && btn.name == 'cancel'){
16873             this.tickItems = Roo.apply([], this.item);
16874             this.collapse();
16875             return;
16876         }
16877         
16878         this.clearItem();
16879         
16880         var _this = this;
16881         
16882         Roo.each(this.tickItems, function(o){
16883             _this.addItem(o);
16884         });
16885         
16886         this.collapse();
16887         
16888     },
16889     
16890     validate : function()
16891     {
16892         if(this.getVisibilityEl().hasClass('hidden')){
16893             return true;
16894         }
16895         
16896         var v = this.getRawValue();
16897         
16898         if(this.multiple){
16899             v = this.getValue();
16900         }
16901         
16902         if(this.disabled || this.allowBlank || v.length){
16903             this.markValid();
16904             return true;
16905         }
16906         
16907         this.markInvalid();
16908         return false;
16909     },
16910     
16911     tickableInputEl : function()
16912     {
16913         if(!this.tickable || !this.editable){
16914             return this.inputEl();
16915         }
16916         
16917         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16918     },
16919     
16920     
16921     getAutoCreateTouchView : function()
16922     {
16923         var id = Roo.id();
16924         
16925         var cfg = {
16926             cls: 'form-group' //input-group
16927         };
16928         
16929         var input =  {
16930             tag: 'input',
16931             id : id,
16932             type : this.inputType,
16933             cls : 'form-control x-combo-noedit',
16934             autocomplete: 'new-password',
16935             placeholder : this.placeholder || '',
16936             readonly : true
16937         };
16938         
16939         if (this.name) {
16940             input.name = this.name;
16941         }
16942         
16943         if (this.size) {
16944             input.cls += ' input-' + this.size;
16945         }
16946         
16947         if (this.disabled) {
16948             input.disabled = true;
16949         }
16950         
16951         var inputblock = {
16952             cls : 'roo-combobox-wrap',
16953             cn : [
16954                 input
16955             ]
16956         };
16957         
16958         if(this.before){
16959             inputblock.cls += ' input-group';
16960             
16961             inputblock.cn.unshift({
16962                 tag :'span',
16963                 cls : 'input-group-addon input-group-prepend input-group-text',
16964                 html : this.before
16965             });
16966         }
16967         
16968         if(this.removable && !this.multiple){
16969             inputblock.cls += ' roo-removable';
16970             
16971             inputblock.cn.push({
16972                 tag: 'button',
16973                 html : 'x',
16974                 cls : 'roo-combo-removable-btn close'
16975             });
16976         }
16977
16978         if(this.hasFeedback && !this.allowBlank){
16979             
16980             inputblock.cls += ' has-feedback';
16981             
16982             inputblock.cn.push({
16983                 tag: 'span',
16984                 cls: 'glyphicon form-control-feedback'
16985             });
16986             
16987         }
16988         
16989         if (this.after) {
16990             
16991             inputblock.cls += (this.before) ? '' : ' input-group';
16992             
16993             inputblock.cn.push({
16994                 tag :'span',
16995                 cls : 'input-group-addon input-group-append input-group-text',
16996                 html : this.after
16997             });
16998         }
16999
17000         
17001         var ibwrap = inputblock;
17002         
17003         if(this.multiple){
17004             ibwrap = {
17005                 tag: 'ul',
17006                 cls: 'roo-select2-choices',
17007                 cn:[
17008                     {
17009                         tag: 'li',
17010                         cls: 'roo-select2-search-field',
17011                         cn: [
17012
17013                             inputblock
17014                         ]
17015                     }
17016                 ]
17017             };
17018         
17019             
17020         }
17021         
17022         var combobox = {
17023             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17024             cn: [
17025                 {
17026                     tag: 'input',
17027                     type : 'hidden',
17028                     cls: 'form-hidden-field'
17029                 },
17030                 ibwrap
17031             ]
17032         };
17033         
17034         if(!this.multiple && this.showToggleBtn){
17035             
17036             var caret = {
17037                 cls: 'caret'
17038             };
17039             
17040             if (this.caret != false) {
17041                 caret = {
17042                      tag: 'i',
17043                      cls: 'fa fa-' + this.caret
17044                 };
17045                 
17046             }
17047             
17048             combobox.cn.push({
17049                 tag :'span',
17050                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17051                 cn : [
17052                     Roo.bootstrap.version == 3 ? caret : '',
17053                     {
17054                         tag: 'span',
17055                         cls: 'combobox-clear',
17056                         cn  : [
17057                             {
17058                                 tag : 'i',
17059                                 cls: 'icon-remove'
17060                             }
17061                         ]
17062                     }
17063                 ]
17064
17065             })
17066         }
17067         
17068         if(this.multiple){
17069             combobox.cls += ' roo-select2-container-multi';
17070         }
17071         
17072         var align = this.labelAlign || this.parentLabelAlign();
17073         
17074         if (align ==='left' && this.fieldLabel.length) {
17075
17076             cfg.cn = [
17077                 {
17078                    tag : 'i',
17079                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17080                    tooltip : 'This field is required'
17081                 },
17082                 {
17083                     tag: 'label',
17084                     cls : 'control-label col-form-label',
17085                     html : this.fieldLabel
17086
17087                 },
17088                 {
17089                     cls : 'roo-combobox-wrap ', 
17090                     cn: [
17091                         combobox
17092                     ]
17093                 }
17094             ];
17095             
17096             var labelCfg = cfg.cn[1];
17097             var contentCfg = cfg.cn[2];
17098             
17099
17100             if(this.indicatorpos == 'right'){
17101                 cfg.cn = [
17102                     {
17103                         tag: 'label',
17104                         'for' :  id,
17105                         cls : 'control-label col-form-label',
17106                         cn : [
17107                             {
17108                                 tag : 'span',
17109                                 html : this.fieldLabel
17110                             },
17111                             {
17112                                 tag : 'i',
17113                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17114                                 tooltip : 'This field is required'
17115                             }
17116                         ]
17117                     },
17118                     {
17119                         cls : "roo-combobox-wrap ",
17120                         cn: [
17121                             combobox
17122                         ]
17123                     }
17124
17125                 ];
17126                 
17127                 labelCfg = cfg.cn[0];
17128                 contentCfg = cfg.cn[1];
17129             }
17130             
17131            
17132             
17133             if(this.labelWidth > 12){
17134                 labelCfg.style = "width: " + this.labelWidth + 'px';
17135             }
17136            
17137             if(this.labelWidth < 13 && this.labelmd == 0){
17138                 this.labelmd = this.labelWidth;
17139             }
17140             
17141             if(this.labellg > 0){
17142                 labelCfg.cls += ' col-lg-' + this.labellg;
17143                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17144             }
17145             
17146             if(this.labelmd > 0){
17147                 labelCfg.cls += ' col-md-' + this.labelmd;
17148                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17149             }
17150             
17151             if(this.labelsm > 0){
17152                 labelCfg.cls += ' col-sm-' + this.labelsm;
17153                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17154             }
17155             
17156             if(this.labelxs > 0){
17157                 labelCfg.cls += ' col-xs-' + this.labelxs;
17158                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17159             }
17160                 
17161                 
17162         } else if ( this.fieldLabel.length) {
17163             cfg.cn = [
17164                 {
17165                    tag : 'i',
17166                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17167                    tooltip : 'This field is required'
17168                 },
17169                 {
17170                     tag: 'label',
17171                     cls : 'control-label',
17172                     html : this.fieldLabel
17173
17174                 },
17175                 {
17176                     cls : '', 
17177                     cn: [
17178                         combobox
17179                     ]
17180                 }
17181             ];
17182             
17183             if(this.indicatorpos == 'right'){
17184                 cfg.cn = [
17185                     {
17186                         tag: 'label',
17187                         cls : 'control-label',
17188                         html : this.fieldLabel,
17189                         cn : [
17190                             {
17191                                tag : 'i',
17192                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17193                                tooltip : 'This field is required'
17194                             }
17195                         ]
17196                     },
17197                     {
17198                         cls : '', 
17199                         cn: [
17200                             combobox
17201                         ]
17202                     }
17203                 ];
17204             }
17205         } else {
17206             cfg.cn = combobox;    
17207         }
17208         
17209         
17210         var settings = this;
17211         
17212         ['xs','sm','md','lg'].map(function(size){
17213             if (settings[size]) {
17214                 cfg.cls += ' col-' + size + '-' + settings[size];
17215             }
17216         });
17217         
17218         return cfg;
17219     },
17220     
17221     initTouchView : function()
17222     {
17223         this.renderTouchView();
17224         
17225         this.touchViewEl.on('scroll', function(){
17226             this.el.dom.scrollTop = 0;
17227         }, this);
17228         
17229         this.originalValue = this.getValue();
17230         
17231         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17232         
17233         this.inputEl().on("click", this.showTouchView, this);
17234         if (this.triggerEl) {
17235             this.triggerEl.on("click", this.showTouchView, this);
17236         }
17237         
17238         
17239         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17240         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17241         
17242         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17243         
17244         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17245         this.store.on('load', this.onTouchViewLoad, this);
17246         this.store.on('loadexception', this.onTouchViewLoadException, this);
17247         
17248         if(this.hiddenName){
17249             
17250             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17251             
17252             this.hiddenField.dom.value =
17253                 this.hiddenValue !== undefined ? this.hiddenValue :
17254                 this.value !== undefined ? this.value : '';
17255         
17256             this.el.dom.removeAttribute('name');
17257             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17258         }
17259         
17260         if(this.multiple){
17261             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17262             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17263         }
17264         
17265         if(this.removable && !this.multiple){
17266             var close = this.closeTriggerEl();
17267             if(close){
17268                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17269                 close.on('click', this.removeBtnClick, this, close);
17270             }
17271         }
17272         /*
17273          * fix the bug in Safari iOS8
17274          */
17275         this.inputEl().on("focus", function(e){
17276             document.activeElement.blur();
17277         }, this);
17278         
17279         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17280         
17281         return;
17282         
17283         
17284     },
17285     
17286     renderTouchView : function()
17287     {
17288         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17289         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17290         
17291         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17292         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17293         
17294         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17295         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17296         this.touchViewBodyEl.setStyle('overflow', 'auto');
17297         
17298         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17299         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17300         
17301         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17302         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17303         
17304     },
17305     
17306     showTouchView : function()
17307     {
17308         if(this.disabled){
17309             return;
17310         }
17311         
17312         this.touchViewHeaderEl.hide();
17313
17314         if(this.modalTitle.length){
17315             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17316             this.touchViewHeaderEl.show();
17317         }
17318
17319         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17320         this.touchViewEl.show();
17321
17322         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17323         
17324         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17325         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17326
17327         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17328
17329         if(this.modalTitle.length){
17330             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17331         }
17332         
17333         this.touchViewBodyEl.setHeight(bodyHeight);
17334
17335         if(this.animate){
17336             var _this = this;
17337             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17338         }else{
17339             this.touchViewEl.addClass(['in','show']);
17340         }
17341         
17342         if(this._touchViewMask){
17343             Roo.get(document.body).addClass("x-body-masked");
17344             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17345             this._touchViewMask.setStyle('z-index', 10000);
17346             this._touchViewMask.addClass('show');
17347         }
17348         
17349         this.doTouchViewQuery();
17350         
17351     },
17352     
17353     hideTouchView : function()
17354     {
17355         this.touchViewEl.removeClass(['in','show']);
17356
17357         if(this.animate){
17358             var _this = this;
17359             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17360         }else{
17361             this.touchViewEl.setStyle('display', 'none');
17362         }
17363         
17364         if(this._touchViewMask){
17365             this._touchViewMask.removeClass('show');
17366             Roo.get(document.body).removeClass("x-body-masked");
17367         }
17368     },
17369     
17370     setTouchViewValue : function()
17371     {
17372         if(this.multiple){
17373             this.clearItem();
17374         
17375             var _this = this;
17376
17377             Roo.each(this.tickItems, function(o){
17378                 this.addItem(o);
17379             }, this);
17380         }
17381         
17382         this.hideTouchView();
17383     },
17384     
17385     doTouchViewQuery : function()
17386     {
17387         var qe = {
17388             query: '',
17389             forceAll: true,
17390             combo: this,
17391             cancel:false
17392         };
17393         
17394         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17395             return false;
17396         }
17397         
17398         if(!this.alwaysQuery || this.mode == 'local'){
17399             this.onTouchViewLoad();
17400             return;
17401         }
17402         
17403         this.store.load();
17404     },
17405     
17406     onTouchViewBeforeLoad : function(combo,opts)
17407     {
17408         return;
17409     },
17410
17411     // private
17412     onTouchViewLoad : function()
17413     {
17414         if(this.store.getCount() < 1){
17415             this.onTouchViewEmptyResults();
17416             return;
17417         }
17418         
17419         this.clearTouchView();
17420         
17421         var rawValue = this.getRawValue();
17422         
17423         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17424         
17425         this.tickItems = [];
17426         
17427         this.store.data.each(function(d, rowIndex){
17428             var row = this.touchViewListGroup.createChild(template);
17429             
17430             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17431                 row.addClass(d.data.cls);
17432             }
17433             
17434             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17435                 var cfg = {
17436                     data : d.data,
17437                     html : d.data[this.displayField]
17438                 };
17439                 
17440                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17441                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17442                 }
17443             }
17444             row.removeClass('selected');
17445             if(!this.multiple && this.valueField &&
17446                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17447             {
17448                 // radio buttons..
17449                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17450                 row.addClass('selected');
17451             }
17452             
17453             if(this.multiple && this.valueField &&
17454                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17455             {
17456                 
17457                 // checkboxes...
17458                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17459                 this.tickItems.push(d.data);
17460             }
17461             
17462             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17463             
17464         }, this);
17465         
17466         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17467         
17468         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17469
17470         if(this.modalTitle.length){
17471             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17472         }
17473
17474         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17475         
17476         if(this.mobile_restrict_height && listHeight < bodyHeight){
17477             this.touchViewBodyEl.setHeight(listHeight);
17478         }
17479         
17480         var _this = this;
17481         
17482         if(firstChecked && listHeight > bodyHeight){
17483             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17484         }
17485         
17486     },
17487     
17488     onTouchViewLoadException : function()
17489     {
17490         this.hideTouchView();
17491     },
17492     
17493     onTouchViewEmptyResults : function()
17494     {
17495         this.clearTouchView();
17496         
17497         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17498         
17499         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17500         
17501     },
17502     
17503     clearTouchView : function()
17504     {
17505         this.touchViewListGroup.dom.innerHTML = '';
17506     },
17507     
17508     onTouchViewClick : function(e, el, o)
17509     {
17510         e.preventDefault();
17511         
17512         var row = o.row;
17513         var rowIndex = o.rowIndex;
17514         
17515         var r = this.store.getAt(rowIndex);
17516         
17517         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17518             
17519             if(!this.multiple){
17520                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17521                     c.dom.removeAttribute('checked');
17522                 }, this);
17523
17524                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17525
17526                 this.setFromData(r.data);
17527
17528                 var close = this.closeTriggerEl();
17529
17530                 if(close){
17531                     close.show();
17532                 }
17533
17534                 this.hideTouchView();
17535
17536                 this.fireEvent('select', this, r, rowIndex);
17537
17538                 return;
17539             }
17540
17541             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17542                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17543                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17544                 return;
17545             }
17546
17547             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17548             this.addItem(r.data);
17549             this.tickItems.push(r.data);
17550         }
17551     },
17552     
17553     getAutoCreateNativeIOS : function()
17554     {
17555         var cfg = {
17556             cls: 'form-group' //input-group,
17557         };
17558         
17559         var combobox =  {
17560             tag: 'select',
17561             cls : 'roo-ios-select'
17562         };
17563         
17564         if (this.name) {
17565             combobox.name = this.name;
17566         }
17567         
17568         if (this.disabled) {
17569             combobox.disabled = true;
17570         }
17571         
17572         var settings = this;
17573         
17574         ['xs','sm','md','lg'].map(function(size){
17575             if (settings[size]) {
17576                 cfg.cls += ' col-' + size + '-' + settings[size];
17577             }
17578         });
17579         
17580         cfg.cn = combobox;
17581         
17582         return cfg;
17583         
17584     },
17585     
17586     initIOSView : function()
17587     {
17588         this.store.on('load', this.onIOSViewLoad, this);
17589         
17590         return;
17591     },
17592     
17593     onIOSViewLoad : function()
17594     {
17595         if(this.store.getCount() < 1){
17596             return;
17597         }
17598         
17599         this.clearIOSView();
17600         
17601         if(this.allowBlank) {
17602             
17603             var default_text = '-- SELECT --';
17604             
17605             if(this.placeholder.length){
17606                 default_text = this.placeholder;
17607             }
17608             
17609             if(this.emptyTitle.length){
17610                 default_text += ' - ' + this.emptyTitle + ' -';
17611             }
17612             
17613             var opt = this.inputEl().createChild({
17614                 tag: 'option',
17615                 value : 0,
17616                 html : default_text
17617             });
17618             
17619             var o = {};
17620             o[this.valueField] = 0;
17621             o[this.displayField] = default_text;
17622             
17623             this.ios_options.push({
17624                 data : o,
17625                 el : opt
17626             });
17627             
17628         }
17629         
17630         this.store.data.each(function(d, rowIndex){
17631             
17632             var html = '';
17633             
17634             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17635                 html = d.data[this.displayField];
17636             }
17637             
17638             var value = '';
17639             
17640             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17641                 value = d.data[this.valueField];
17642             }
17643             
17644             var option = {
17645                 tag: 'option',
17646                 value : value,
17647                 html : html
17648             };
17649             
17650             if(this.value == d.data[this.valueField]){
17651                 option['selected'] = true;
17652             }
17653             
17654             var opt = this.inputEl().createChild(option);
17655             
17656             this.ios_options.push({
17657                 data : d.data,
17658                 el : opt
17659             });
17660             
17661         }, this);
17662         
17663         this.inputEl().on('change', function(){
17664            this.fireEvent('select', this);
17665         }, this);
17666         
17667     },
17668     
17669     clearIOSView: function()
17670     {
17671         this.inputEl().dom.innerHTML = '';
17672         
17673         this.ios_options = [];
17674     },
17675     
17676     setIOSValue: function(v)
17677     {
17678         this.value = v;
17679         
17680         if(!this.ios_options){
17681             return;
17682         }
17683         
17684         Roo.each(this.ios_options, function(opts){
17685            
17686            opts.el.dom.removeAttribute('selected');
17687            
17688            if(opts.data[this.valueField] != v){
17689                return;
17690            }
17691            
17692            opts.el.dom.setAttribute('selected', true);
17693            
17694         }, this);
17695     }
17696
17697     /** 
17698     * @cfg {Boolean} grow 
17699     * @hide 
17700     */
17701     /** 
17702     * @cfg {Number} growMin 
17703     * @hide 
17704     */
17705     /** 
17706     * @cfg {Number} growMax 
17707     * @hide 
17708     */
17709     /**
17710      * @hide
17711      * @method autoSize
17712      */
17713 });
17714
17715 Roo.apply(Roo.bootstrap.ComboBox,  {
17716     
17717     header : {
17718         tag: 'div',
17719         cls: 'modal-header',
17720         cn: [
17721             {
17722                 tag: 'h4',
17723                 cls: 'modal-title'
17724             }
17725         ]
17726     },
17727     
17728     body : {
17729         tag: 'div',
17730         cls: 'modal-body',
17731         cn: [
17732             {
17733                 tag: 'ul',
17734                 cls: 'list-group'
17735             }
17736         ]
17737     },
17738     
17739     listItemRadio : {
17740         tag: 'li',
17741         cls: 'list-group-item',
17742         cn: [
17743             {
17744                 tag: 'span',
17745                 cls: 'roo-combobox-list-group-item-value'
17746             },
17747             {
17748                 tag: 'div',
17749                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17750                 cn: [
17751                     {
17752                         tag: 'input',
17753                         type: 'radio'
17754                     },
17755                     {
17756                         tag: 'label'
17757                     }
17758                 ]
17759             }
17760         ]
17761     },
17762     
17763     listItemCheckbox : {
17764         tag: 'li',
17765         cls: 'list-group-item',
17766         cn: [
17767             {
17768                 tag: 'span',
17769                 cls: 'roo-combobox-list-group-item-value'
17770             },
17771             {
17772                 tag: 'div',
17773                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17774                 cn: [
17775                     {
17776                         tag: 'input',
17777                         type: 'checkbox'
17778                     },
17779                     {
17780                         tag: 'label'
17781                     }
17782                 ]
17783             }
17784         ]
17785     },
17786     
17787     emptyResult : {
17788         tag: 'div',
17789         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17790     },
17791     
17792     footer : {
17793         tag: 'div',
17794         cls: 'modal-footer',
17795         cn: [
17796             {
17797                 tag: 'div',
17798                 cls: 'row',
17799                 cn: [
17800                     {
17801                         tag: 'div',
17802                         cls: 'col-xs-6 text-left',
17803                         cn: {
17804                             tag: 'button',
17805                             cls: 'btn btn-danger roo-touch-view-cancel',
17806                             html: 'Cancel'
17807                         }
17808                     },
17809                     {
17810                         tag: 'div',
17811                         cls: 'col-xs-6 text-right',
17812                         cn: {
17813                             tag: 'button',
17814                             cls: 'btn btn-success roo-touch-view-ok',
17815                             html: 'OK'
17816                         }
17817                     }
17818                 ]
17819             }
17820         ]
17821         
17822     }
17823 });
17824
17825 Roo.apply(Roo.bootstrap.ComboBox,  {
17826     
17827     touchViewTemplate : {
17828         tag: 'div',
17829         cls: 'modal fade roo-combobox-touch-view',
17830         cn: [
17831             {
17832                 tag: 'div',
17833                 cls: 'modal-dialog',
17834                 style : 'position:fixed', // we have to fix position....
17835                 cn: [
17836                     {
17837                         tag: 'div',
17838                         cls: 'modal-content',
17839                         cn: [
17840                             Roo.bootstrap.ComboBox.header,
17841                             Roo.bootstrap.ComboBox.body,
17842                             Roo.bootstrap.ComboBox.footer
17843                         ]
17844                     }
17845                 ]
17846             }
17847         ]
17848     }
17849 });/*
17850  * Based on:
17851  * Ext JS Library 1.1.1
17852  * Copyright(c) 2006-2007, Ext JS, LLC.
17853  *
17854  * Originally Released Under LGPL - original licence link has changed is not relivant.
17855  *
17856  * Fork - LGPL
17857  * <script type="text/javascript">
17858  */
17859
17860 /**
17861  * @class Roo.View
17862  * @extends Roo.util.Observable
17863  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17864  * This class also supports single and multi selection modes. <br>
17865  * Create a data model bound view:
17866  <pre><code>
17867  var store = new Roo.data.Store(...);
17868
17869  var view = new Roo.View({
17870     el : "my-element",
17871     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17872  
17873     singleSelect: true,
17874     selectedClass: "ydataview-selected",
17875     store: store
17876  });
17877
17878  // listen for node click?
17879  view.on("click", function(vw, index, node, e){
17880  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17881  });
17882
17883  // load XML data
17884  dataModel.load("foobar.xml");
17885  </code></pre>
17886  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17887  * <br><br>
17888  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17889  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17890  * 
17891  * Note: old style constructor is still suported (container, template, config)
17892  * 
17893  * @constructor
17894  * Create a new View
17895  * @param {Object} config The config object
17896  * 
17897  */
17898 Roo.View = function(config, depreciated_tpl, depreciated_config){
17899     
17900     this.parent = false;
17901     
17902     if (typeof(depreciated_tpl) == 'undefined') {
17903         // new way.. - universal constructor.
17904         Roo.apply(this, config);
17905         this.el  = Roo.get(this.el);
17906     } else {
17907         // old format..
17908         this.el  = Roo.get(config);
17909         this.tpl = depreciated_tpl;
17910         Roo.apply(this, depreciated_config);
17911     }
17912     this.wrapEl  = this.el.wrap().wrap();
17913     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17914     
17915     
17916     if(typeof(this.tpl) == "string"){
17917         this.tpl = new Roo.Template(this.tpl);
17918     } else {
17919         // support xtype ctors..
17920         this.tpl = new Roo.factory(this.tpl, Roo);
17921     }
17922     
17923     
17924     this.tpl.compile();
17925     
17926     /** @private */
17927     this.addEvents({
17928         /**
17929          * @event beforeclick
17930          * Fires before a click is processed. Returns false to cancel the default action.
17931          * @param {Roo.View} this
17932          * @param {Number} index The index of the target node
17933          * @param {HTMLElement} node The target node
17934          * @param {Roo.EventObject} e The raw event object
17935          */
17936             "beforeclick" : true,
17937         /**
17938          * @event click
17939          * Fires when a template node is clicked.
17940          * @param {Roo.View} this
17941          * @param {Number} index The index of the target node
17942          * @param {HTMLElement} node The target node
17943          * @param {Roo.EventObject} e The raw event object
17944          */
17945             "click" : true,
17946         /**
17947          * @event dblclick
17948          * Fires when a template node is double clicked.
17949          * @param {Roo.View} this
17950          * @param {Number} index The index of the target node
17951          * @param {HTMLElement} node The target node
17952          * @param {Roo.EventObject} e The raw event object
17953          */
17954             "dblclick" : true,
17955         /**
17956          * @event contextmenu
17957          * Fires when a template node is right clicked.
17958          * @param {Roo.View} this
17959          * @param {Number} index The index of the target node
17960          * @param {HTMLElement} node The target node
17961          * @param {Roo.EventObject} e The raw event object
17962          */
17963             "contextmenu" : true,
17964         /**
17965          * @event selectionchange
17966          * Fires when the selected nodes change.
17967          * @param {Roo.View} this
17968          * @param {Array} selections Array of the selected nodes
17969          */
17970             "selectionchange" : true,
17971     
17972         /**
17973          * @event beforeselect
17974          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17975          * @param {Roo.View} this
17976          * @param {HTMLElement} node The node to be selected
17977          * @param {Array} selections Array of currently selected nodes
17978          */
17979             "beforeselect" : true,
17980         /**
17981          * @event preparedata
17982          * Fires on every row to render, to allow you to change the data.
17983          * @param {Roo.View} this
17984          * @param {Object} data to be rendered (change this)
17985          */
17986           "preparedata" : true
17987           
17988           
17989         });
17990
17991
17992
17993     this.el.on({
17994         "click": this.onClick,
17995         "dblclick": this.onDblClick,
17996         "contextmenu": this.onContextMenu,
17997         scope:this
17998     });
17999
18000     this.selections = [];
18001     this.nodes = [];
18002     this.cmp = new Roo.CompositeElementLite([]);
18003     if(this.store){
18004         this.store = Roo.factory(this.store, Roo.data);
18005         this.setStore(this.store, true);
18006     }
18007     
18008     if ( this.footer && this.footer.xtype) {
18009            
18010          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18011         
18012         this.footer.dataSource = this.store;
18013         this.footer.container = fctr;
18014         this.footer = Roo.factory(this.footer, Roo);
18015         fctr.insertFirst(this.el);
18016         
18017         // this is a bit insane - as the paging toolbar seems to detach the el..
18018 //        dom.parentNode.parentNode.parentNode
18019          // they get detached?
18020     }
18021     
18022     
18023     Roo.View.superclass.constructor.call(this);
18024     
18025     
18026 };
18027
18028 Roo.extend(Roo.View, Roo.util.Observable, {
18029     
18030      /**
18031      * @cfg {Roo.data.Store} store Data store to load data from.
18032      */
18033     store : false,
18034     
18035     /**
18036      * @cfg {String|Roo.Element} el The container element.
18037      */
18038     el : '',
18039     
18040     /**
18041      * @cfg {String|Roo.Template} tpl The template used by this View 
18042      */
18043     tpl : false,
18044     /**
18045      * @cfg {String} dataName the named area of the template to use as the data area
18046      *                          Works with domtemplates roo-name="name"
18047      */
18048     dataName: false,
18049     /**
18050      * @cfg {String} selectedClass The css class to add to selected nodes
18051      */
18052     selectedClass : "x-view-selected",
18053      /**
18054      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18055      */
18056     emptyText : "",
18057     
18058     /**
18059      * @cfg {String} text to display on mask (default Loading)
18060      */
18061     mask : false,
18062     /**
18063      * @cfg {Boolean} multiSelect Allow multiple selection
18064      */
18065     multiSelect : false,
18066     /**
18067      * @cfg {Boolean} singleSelect Allow single selection
18068      */
18069     singleSelect:  false,
18070     
18071     /**
18072      * @cfg {Boolean} toggleSelect - selecting 
18073      */
18074     toggleSelect : false,
18075     
18076     /**
18077      * @cfg {Boolean} tickable - selecting 
18078      */
18079     tickable : false,
18080     
18081     /**
18082      * Returns the element this view is bound to.
18083      * @return {Roo.Element}
18084      */
18085     getEl : function(){
18086         return this.wrapEl;
18087     },
18088     
18089     
18090
18091     /**
18092      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18093      */
18094     refresh : function(){
18095         //Roo.log('refresh');
18096         var t = this.tpl;
18097         
18098         // if we are using something like 'domtemplate', then
18099         // the what gets used is:
18100         // t.applySubtemplate(NAME, data, wrapping data..)
18101         // the outer template then get' applied with
18102         //     the store 'extra data'
18103         // and the body get's added to the
18104         //      roo-name="data" node?
18105         //      <span class='roo-tpl-{name}'></span> ?????
18106         
18107         
18108         
18109         this.clearSelections();
18110         this.el.update("");
18111         var html = [];
18112         var records = this.store.getRange();
18113         if(records.length < 1) {
18114             
18115             // is this valid??  = should it render a template??
18116             
18117             this.el.update(this.emptyText);
18118             return;
18119         }
18120         var el = this.el;
18121         if (this.dataName) {
18122             this.el.update(t.apply(this.store.meta)); //????
18123             el = this.el.child('.roo-tpl-' + this.dataName);
18124         }
18125         
18126         for(var i = 0, len = records.length; i < len; i++){
18127             var data = this.prepareData(records[i].data, i, records[i]);
18128             this.fireEvent("preparedata", this, data, i, records[i]);
18129             
18130             var d = Roo.apply({}, data);
18131             
18132             if(this.tickable){
18133                 Roo.apply(d, {'roo-id' : Roo.id()});
18134                 
18135                 var _this = this;
18136             
18137                 Roo.each(this.parent.item, function(item){
18138                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18139                         return;
18140                     }
18141                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18142                 });
18143             }
18144             
18145             html[html.length] = Roo.util.Format.trim(
18146                 this.dataName ?
18147                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18148                     t.apply(d)
18149             );
18150         }
18151         
18152         
18153         
18154         el.update(html.join(""));
18155         this.nodes = el.dom.childNodes;
18156         this.updateIndexes(0);
18157     },
18158     
18159
18160     /**
18161      * Function to override to reformat the data that is sent to
18162      * the template for each node.
18163      * DEPRICATED - use the preparedata event handler.
18164      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18165      * a JSON object for an UpdateManager bound view).
18166      */
18167     prepareData : function(data, index, record)
18168     {
18169         this.fireEvent("preparedata", this, data, index, record);
18170         return data;
18171     },
18172
18173     onUpdate : function(ds, record){
18174         // Roo.log('on update');   
18175         this.clearSelections();
18176         var index = this.store.indexOf(record);
18177         var n = this.nodes[index];
18178         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18179         n.parentNode.removeChild(n);
18180         this.updateIndexes(index, index);
18181     },
18182
18183     
18184     
18185 // --------- FIXME     
18186     onAdd : function(ds, records, index)
18187     {
18188         //Roo.log(['on Add', ds, records, index] );        
18189         this.clearSelections();
18190         if(this.nodes.length == 0){
18191             this.refresh();
18192             return;
18193         }
18194         var n = this.nodes[index];
18195         for(var i = 0, len = records.length; i < len; i++){
18196             var d = this.prepareData(records[i].data, i, records[i]);
18197             if(n){
18198                 this.tpl.insertBefore(n, d);
18199             }else{
18200                 
18201                 this.tpl.append(this.el, d);
18202             }
18203         }
18204         this.updateIndexes(index);
18205     },
18206
18207     onRemove : function(ds, record, index){
18208        // Roo.log('onRemove');
18209         this.clearSelections();
18210         var el = this.dataName  ?
18211             this.el.child('.roo-tpl-' + this.dataName) :
18212             this.el; 
18213         
18214         el.dom.removeChild(this.nodes[index]);
18215         this.updateIndexes(index);
18216     },
18217
18218     /**
18219      * Refresh an individual node.
18220      * @param {Number} index
18221      */
18222     refreshNode : function(index){
18223         this.onUpdate(this.store, this.store.getAt(index));
18224     },
18225
18226     updateIndexes : function(startIndex, endIndex){
18227         var ns = this.nodes;
18228         startIndex = startIndex || 0;
18229         endIndex = endIndex || ns.length - 1;
18230         for(var i = startIndex; i <= endIndex; i++){
18231             ns[i].nodeIndex = i;
18232         }
18233     },
18234
18235     /**
18236      * Changes the data store this view uses and refresh the view.
18237      * @param {Store} store
18238      */
18239     setStore : function(store, initial){
18240         if(!initial && this.store){
18241             this.store.un("datachanged", this.refresh);
18242             this.store.un("add", this.onAdd);
18243             this.store.un("remove", this.onRemove);
18244             this.store.un("update", this.onUpdate);
18245             this.store.un("clear", this.refresh);
18246             this.store.un("beforeload", this.onBeforeLoad);
18247             this.store.un("load", this.onLoad);
18248             this.store.un("loadexception", this.onLoad);
18249         }
18250         if(store){
18251           
18252             store.on("datachanged", this.refresh, this);
18253             store.on("add", this.onAdd, this);
18254             store.on("remove", this.onRemove, this);
18255             store.on("update", this.onUpdate, this);
18256             store.on("clear", this.refresh, this);
18257             store.on("beforeload", this.onBeforeLoad, this);
18258             store.on("load", this.onLoad, this);
18259             store.on("loadexception", this.onLoad, this);
18260         }
18261         
18262         if(store){
18263             this.refresh();
18264         }
18265     },
18266     /**
18267      * onbeforeLoad - masks the loading area.
18268      *
18269      */
18270     onBeforeLoad : function(store,opts)
18271     {
18272          //Roo.log('onBeforeLoad');   
18273         if (!opts.add) {
18274             this.el.update("");
18275         }
18276         this.el.mask(this.mask ? this.mask : "Loading" ); 
18277     },
18278     onLoad : function ()
18279     {
18280         this.el.unmask();
18281     },
18282     
18283
18284     /**
18285      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18286      * @param {HTMLElement} node
18287      * @return {HTMLElement} The template node
18288      */
18289     findItemFromChild : function(node){
18290         var el = this.dataName  ?
18291             this.el.child('.roo-tpl-' + this.dataName,true) :
18292             this.el.dom; 
18293         
18294         if(!node || node.parentNode == el){
18295                     return node;
18296             }
18297             var p = node.parentNode;
18298             while(p && p != el){
18299             if(p.parentNode == el){
18300                 return p;
18301             }
18302             p = p.parentNode;
18303         }
18304             return null;
18305     },
18306
18307     /** @ignore */
18308     onClick : function(e){
18309         var item = this.findItemFromChild(e.getTarget());
18310         if(item){
18311             var index = this.indexOf(item);
18312             if(this.onItemClick(item, index, e) !== false){
18313                 this.fireEvent("click", this, index, item, e);
18314             }
18315         }else{
18316             this.clearSelections();
18317         }
18318     },
18319
18320     /** @ignore */
18321     onContextMenu : function(e){
18322         var item = this.findItemFromChild(e.getTarget());
18323         if(item){
18324             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18325         }
18326     },
18327
18328     /** @ignore */
18329     onDblClick : function(e){
18330         var item = this.findItemFromChild(e.getTarget());
18331         if(item){
18332             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18333         }
18334     },
18335
18336     onItemClick : function(item, index, e)
18337     {
18338         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18339             return false;
18340         }
18341         if (this.toggleSelect) {
18342             var m = this.isSelected(item) ? 'unselect' : 'select';
18343             //Roo.log(m);
18344             var _t = this;
18345             _t[m](item, true, false);
18346             return true;
18347         }
18348         if(this.multiSelect || this.singleSelect){
18349             if(this.multiSelect && e.shiftKey && this.lastSelection){
18350                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18351             }else{
18352                 this.select(item, this.multiSelect && e.ctrlKey);
18353                 this.lastSelection = item;
18354             }
18355             
18356             if(!this.tickable){
18357                 e.preventDefault();
18358             }
18359             
18360         }
18361         return true;
18362     },
18363
18364     /**
18365      * Get the number of selected nodes.
18366      * @return {Number}
18367      */
18368     getSelectionCount : function(){
18369         return this.selections.length;
18370     },
18371
18372     /**
18373      * Get the currently selected nodes.
18374      * @return {Array} An array of HTMLElements
18375      */
18376     getSelectedNodes : function(){
18377         return this.selections;
18378     },
18379
18380     /**
18381      * Get the indexes of the selected nodes.
18382      * @return {Array}
18383      */
18384     getSelectedIndexes : function(){
18385         var indexes = [], s = this.selections;
18386         for(var i = 0, len = s.length; i < len; i++){
18387             indexes.push(s[i].nodeIndex);
18388         }
18389         return indexes;
18390     },
18391
18392     /**
18393      * Clear all selections
18394      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18395      */
18396     clearSelections : function(suppressEvent){
18397         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18398             this.cmp.elements = this.selections;
18399             this.cmp.removeClass(this.selectedClass);
18400             this.selections = [];
18401             if(!suppressEvent){
18402                 this.fireEvent("selectionchange", this, this.selections);
18403             }
18404         }
18405     },
18406
18407     /**
18408      * Returns true if the passed node is selected
18409      * @param {HTMLElement/Number} node The node or node index
18410      * @return {Boolean}
18411      */
18412     isSelected : function(node){
18413         var s = this.selections;
18414         if(s.length < 1){
18415             return false;
18416         }
18417         node = this.getNode(node);
18418         return s.indexOf(node) !== -1;
18419     },
18420
18421     /**
18422      * Selects nodes.
18423      * @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
18424      * @param {Boolean} keepExisting (optional) true to keep existing selections
18425      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18426      */
18427     select : function(nodeInfo, keepExisting, suppressEvent){
18428         if(nodeInfo instanceof Array){
18429             if(!keepExisting){
18430                 this.clearSelections(true);
18431             }
18432             for(var i = 0, len = nodeInfo.length; i < len; i++){
18433                 this.select(nodeInfo[i], true, true);
18434             }
18435             return;
18436         } 
18437         var node = this.getNode(nodeInfo);
18438         if(!node || this.isSelected(node)){
18439             return; // already selected.
18440         }
18441         if(!keepExisting){
18442             this.clearSelections(true);
18443         }
18444         
18445         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18446             Roo.fly(node).addClass(this.selectedClass);
18447             this.selections.push(node);
18448             if(!suppressEvent){
18449                 this.fireEvent("selectionchange", this, this.selections);
18450             }
18451         }
18452         
18453         
18454     },
18455       /**
18456      * Unselects nodes.
18457      * @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
18458      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18459      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18460      */
18461     unselect : function(nodeInfo, keepExisting, suppressEvent)
18462     {
18463         if(nodeInfo instanceof Array){
18464             Roo.each(this.selections, function(s) {
18465                 this.unselect(s, nodeInfo);
18466             }, this);
18467             return;
18468         }
18469         var node = this.getNode(nodeInfo);
18470         if(!node || !this.isSelected(node)){
18471             //Roo.log("not selected");
18472             return; // not selected.
18473         }
18474         // fireevent???
18475         var ns = [];
18476         Roo.each(this.selections, function(s) {
18477             if (s == node ) {
18478                 Roo.fly(node).removeClass(this.selectedClass);
18479
18480                 return;
18481             }
18482             ns.push(s);
18483         },this);
18484         
18485         this.selections= ns;
18486         this.fireEvent("selectionchange", this, this.selections);
18487     },
18488
18489     /**
18490      * Gets a template node.
18491      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18492      * @return {HTMLElement} The node or null if it wasn't found
18493      */
18494     getNode : function(nodeInfo){
18495         if(typeof nodeInfo == "string"){
18496             return document.getElementById(nodeInfo);
18497         }else if(typeof nodeInfo == "number"){
18498             return this.nodes[nodeInfo];
18499         }
18500         return nodeInfo;
18501     },
18502
18503     /**
18504      * Gets a range template nodes.
18505      * @param {Number} startIndex
18506      * @param {Number} endIndex
18507      * @return {Array} An array of nodes
18508      */
18509     getNodes : function(start, end){
18510         var ns = this.nodes;
18511         start = start || 0;
18512         end = typeof end == "undefined" ? ns.length - 1 : end;
18513         var nodes = [];
18514         if(start <= end){
18515             for(var i = start; i <= end; i++){
18516                 nodes.push(ns[i]);
18517             }
18518         } else{
18519             for(var i = start; i >= end; i--){
18520                 nodes.push(ns[i]);
18521             }
18522         }
18523         return nodes;
18524     },
18525
18526     /**
18527      * Finds the index of the passed node
18528      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18529      * @return {Number} The index of the node or -1
18530      */
18531     indexOf : function(node){
18532         node = this.getNode(node);
18533         if(typeof node.nodeIndex == "number"){
18534             return node.nodeIndex;
18535         }
18536         var ns = this.nodes;
18537         for(var i = 0, len = ns.length; i < len; i++){
18538             if(ns[i] == node){
18539                 return i;
18540             }
18541         }
18542         return -1;
18543     }
18544 });
18545 /*
18546  * - LGPL
18547  *
18548  * based on jquery fullcalendar
18549  * 
18550  */
18551
18552 Roo.bootstrap = Roo.bootstrap || {};
18553 /**
18554  * @class Roo.bootstrap.Calendar
18555  * @extends Roo.bootstrap.Component
18556  * Bootstrap Calendar class
18557  * @cfg {Boolean} loadMask (true|false) default false
18558  * @cfg {Object} header generate the user specific header of the calendar, default false
18559
18560  * @constructor
18561  * Create a new Container
18562  * @param {Object} config The config object
18563  */
18564
18565
18566
18567 Roo.bootstrap.Calendar = function(config){
18568     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18569      this.addEvents({
18570         /**
18571              * @event select
18572              * Fires when a date is selected
18573              * @param {DatePicker} this
18574              * @param {Date} date The selected date
18575              */
18576         'select': true,
18577         /**
18578              * @event monthchange
18579              * Fires when the displayed month changes 
18580              * @param {DatePicker} this
18581              * @param {Date} date The selected month
18582              */
18583         'monthchange': true,
18584         /**
18585              * @event evententer
18586              * Fires when mouse over an event
18587              * @param {Calendar} this
18588              * @param {event} Event
18589              */
18590         'evententer': true,
18591         /**
18592              * @event eventleave
18593              * Fires when the mouse leaves an
18594              * @param {Calendar} this
18595              * @param {event}
18596              */
18597         'eventleave': true,
18598         /**
18599              * @event eventclick
18600              * Fires when the mouse click an
18601              * @param {Calendar} this
18602              * @param {event}
18603              */
18604         'eventclick': true
18605         
18606     });
18607
18608 };
18609
18610 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18611     
18612      /**
18613      * @cfg {Number} startDay
18614      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18615      */
18616     startDay : 0,
18617     
18618     loadMask : false,
18619     
18620     header : false,
18621       
18622     getAutoCreate : function(){
18623         
18624         
18625         var fc_button = function(name, corner, style, content ) {
18626             return Roo.apply({},{
18627                 tag : 'span',
18628                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18629                          (corner.length ?
18630                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18631                             ''
18632                         ),
18633                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18634                 unselectable: 'on'
18635             });
18636         };
18637         
18638         var header = {};
18639         
18640         if(!this.header){
18641             header = {
18642                 tag : 'table',
18643                 cls : 'fc-header',
18644                 style : 'width:100%',
18645                 cn : [
18646                     {
18647                         tag: 'tr',
18648                         cn : [
18649                             {
18650                                 tag : 'td',
18651                                 cls : 'fc-header-left',
18652                                 cn : [
18653                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18654                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18655                                     { tag: 'span', cls: 'fc-header-space' },
18656                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18657
18658
18659                                 ]
18660                             },
18661
18662                             {
18663                                 tag : 'td',
18664                                 cls : 'fc-header-center',
18665                                 cn : [
18666                                     {
18667                                         tag: 'span',
18668                                         cls: 'fc-header-title',
18669                                         cn : {
18670                                             tag: 'H2',
18671                                             html : 'month / year'
18672                                         }
18673                                     }
18674
18675                                 ]
18676                             },
18677                             {
18678                                 tag : 'td',
18679                                 cls : 'fc-header-right',
18680                                 cn : [
18681                               /*      fc_button('month', 'left', '', 'month' ),
18682                                     fc_button('week', '', '', 'week' ),
18683                                     fc_button('day', 'right', '', 'day' )
18684                                 */    
18685
18686                                 ]
18687                             }
18688
18689                         ]
18690                     }
18691                 ]
18692             };
18693         }
18694         
18695         header = this.header;
18696         
18697        
18698         var cal_heads = function() {
18699             var ret = [];
18700             // fixme - handle this.
18701             
18702             for (var i =0; i < Date.dayNames.length; i++) {
18703                 var d = Date.dayNames[i];
18704                 ret.push({
18705                     tag: 'th',
18706                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18707                     html : d.substring(0,3)
18708                 });
18709                 
18710             }
18711             ret[0].cls += ' fc-first';
18712             ret[6].cls += ' fc-last';
18713             return ret;
18714         };
18715         var cal_cell = function(n) {
18716             return  {
18717                 tag: 'td',
18718                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18719                 cn : [
18720                     {
18721                         cn : [
18722                             {
18723                                 cls: 'fc-day-number',
18724                                 html: 'D'
18725                             },
18726                             {
18727                                 cls: 'fc-day-content',
18728                              
18729                                 cn : [
18730                                      {
18731                                         style: 'position: relative;' // height: 17px;
18732                                     }
18733                                 ]
18734                             }
18735                             
18736                             
18737                         ]
18738                     }
18739                 ]
18740                 
18741             }
18742         };
18743         var cal_rows = function() {
18744             
18745             var ret = [];
18746             for (var r = 0; r < 6; r++) {
18747                 var row= {
18748                     tag : 'tr',
18749                     cls : 'fc-week',
18750                     cn : []
18751                 };
18752                 
18753                 for (var i =0; i < Date.dayNames.length; i++) {
18754                     var d = Date.dayNames[i];
18755                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18756
18757                 }
18758                 row.cn[0].cls+=' fc-first';
18759                 row.cn[0].cn[0].style = 'min-height:90px';
18760                 row.cn[6].cls+=' fc-last';
18761                 ret.push(row);
18762                 
18763             }
18764             ret[0].cls += ' fc-first';
18765             ret[4].cls += ' fc-prev-last';
18766             ret[5].cls += ' fc-last';
18767             return ret;
18768             
18769         };
18770         
18771         var cal_table = {
18772             tag: 'table',
18773             cls: 'fc-border-separate',
18774             style : 'width:100%',
18775             cellspacing  : 0,
18776             cn : [
18777                 { 
18778                     tag: 'thead',
18779                     cn : [
18780                         { 
18781                             tag: 'tr',
18782                             cls : 'fc-first fc-last',
18783                             cn : cal_heads()
18784                         }
18785                     ]
18786                 },
18787                 { 
18788                     tag: 'tbody',
18789                     cn : cal_rows()
18790                 }
18791                   
18792             ]
18793         };
18794          
18795          var cfg = {
18796             cls : 'fc fc-ltr',
18797             cn : [
18798                 header,
18799                 {
18800                     cls : 'fc-content',
18801                     style : "position: relative;",
18802                     cn : [
18803                         {
18804                             cls : 'fc-view fc-view-month fc-grid',
18805                             style : 'position: relative',
18806                             unselectable : 'on',
18807                             cn : [
18808                                 {
18809                                     cls : 'fc-event-container',
18810                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18811                                 },
18812                                 cal_table
18813                             ]
18814                         }
18815                     ]
18816     
18817                 }
18818            ] 
18819             
18820         };
18821         
18822          
18823         
18824         return cfg;
18825     },
18826     
18827     
18828     initEvents : function()
18829     {
18830         if(!this.store){
18831             throw "can not find store for calendar";
18832         }
18833         
18834         var mark = {
18835             tag: "div",
18836             cls:"x-dlg-mask",
18837             style: "text-align:center",
18838             cn: [
18839                 {
18840                     tag: "div",
18841                     style: "background-color:white;width:50%;margin:250 auto",
18842                     cn: [
18843                         {
18844                             tag: "img",
18845                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18846                         },
18847                         {
18848                             tag: "span",
18849                             html: "Loading"
18850                         }
18851                         
18852                     ]
18853                 }
18854             ]
18855         };
18856         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18857         
18858         var size = this.el.select('.fc-content', true).first().getSize();
18859         this.maskEl.setSize(size.width, size.height);
18860         this.maskEl.enableDisplayMode("block");
18861         if(!this.loadMask){
18862             this.maskEl.hide();
18863         }
18864         
18865         this.store = Roo.factory(this.store, Roo.data);
18866         this.store.on('load', this.onLoad, this);
18867         this.store.on('beforeload', this.onBeforeLoad, this);
18868         
18869         this.resize();
18870         
18871         this.cells = this.el.select('.fc-day',true);
18872         //Roo.log(this.cells);
18873         this.textNodes = this.el.query('.fc-day-number');
18874         this.cells.addClassOnOver('fc-state-hover');
18875         
18876         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18877         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18878         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18879         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18880         
18881         this.on('monthchange', this.onMonthChange, this);
18882         
18883         this.update(new Date().clearTime());
18884     },
18885     
18886     resize : function() {
18887         var sz  = this.el.getSize();
18888         
18889         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18890         this.el.select('.fc-day-content div',true).setHeight(34);
18891     },
18892     
18893     
18894     // private
18895     showPrevMonth : function(e){
18896         this.update(this.activeDate.add("mo", -1));
18897     },
18898     showToday : function(e){
18899         this.update(new Date().clearTime());
18900     },
18901     // private
18902     showNextMonth : function(e){
18903         this.update(this.activeDate.add("mo", 1));
18904     },
18905
18906     // private
18907     showPrevYear : function(){
18908         this.update(this.activeDate.add("y", -1));
18909     },
18910
18911     // private
18912     showNextYear : function(){
18913         this.update(this.activeDate.add("y", 1));
18914     },
18915
18916     
18917    // private
18918     update : function(date)
18919     {
18920         var vd = this.activeDate;
18921         this.activeDate = date;
18922 //        if(vd && this.el){
18923 //            var t = date.getTime();
18924 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18925 //                Roo.log('using add remove');
18926 //                
18927 //                this.fireEvent('monthchange', this, date);
18928 //                
18929 //                this.cells.removeClass("fc-state-highlight");
18930 //                this.cells.each(function(c){
18931 //                   if(c.dateValue == t){
18932 //                       c.addClass("fc-state-highlight");
18933 //                       setTimeout(function(){
18934 //                            try{c.dom.firstChild.focus();}catch(e){}
18935 //                       }, 50);
18936 //                       return false;
18937 //                   }
18938 //                   return true;
18939 //                });
18940 //                return;
18941 //            }
18942 //        }
18943         
18944         var days = date.getDaysInMonth();
18945         
18946         var firstOfMonth = date.getFirstDateOfMonth();
18947         var startingPos = firstOfMonth.getDay()-this.startDay;
18948         
18949         if(startingPos < this.startDay){
18950             startingPos += 7;
18951         }
18952         
18953         var pm = date.add(Date.MONTH, -1);
18954         var prevStart = pm.getDaysInMonth()-startingPos;
18955 //        
18956         this.cells = this.el.select('.fc-day',true);
18957         this.textNodes = this.el.query('.fc-day-number');
18958         this.cells.addClassOnOver('fc-state-hover');
18959         
18960         var cells = this.cells.elements;
18961         var textEls = this.textNodes;
18962         
18963         Roo.each(cells, function(cell){
18964             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18965         });
18966         
18967         days += startingPos;
18968
18969         // convert everything to numbers so it's fast
18970         var day = 86400000;
18971         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18972         //Roo.log(d);
18973         //Roo.log(pm);
18974         //Roo.log(prevStart);
18975         
18976         var today = new Date().clearTime().getTime();
18977         var sel = date.clearTime().getTime();
18978         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18979         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18980         var ddMatch = this.disabledDatesRE;
18981         var ddText = this.disabledDatesText;
18982         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18983         var ddaysText = this.disabledDaysText;
18984         var format = this.format;
18985         
18986         var setCellClass = function(cal, cell){
18987             cell.row = 0;
18988             cell.events = [];
18989             cell.more = [];
18990             //Roo.log('set Cell Class');
18991             cell.title = "";
18992             var t = d.getTime();
18993             
18994             //Roo.log(d);
18995             
18996             cell.dateValue = t;
18997             if(t == today){
18998                 cell.className += " fc-today";
18999                 cell.className += " fc-state-highlight";
19000                 cell.title = cal.todayText;
19001             }
19002             if(t == sel){
19003                 // disable highlight in other month..
19004                 //cell.className += " fc-state-highlight";
19005                 
19006             }
19007             // disabling
19008             if(t < min) {
19009                 cell.className = " fc-state-disabled";
19010                 cell.title = cal.minText;
19011                 return;
19012             }
19013             if(t > max) {
19014                 cell.className = " fc-state-disabled";
19015                 cell.title = cal.maxText;
19016                 return;
19017             }
19018             if(ddays){
19019                 if(ddays.indexOf(d.getDay()) != -1){
19020                     cell.title = ddaysText;
19021                     cell.className = " fc-state-disabled";
19022                 }
19023             }
19024             if(ddMatch && format){
19025                 var fvalue = d.dateFormat(format);
19026                 if(ddMatch.test(fvalue)){
19027                     cell.title = ddText.replace("%0", fvalue);
19028                     cell.className = " fc-state-disabled";
19029                 }
19030             }
19031             
19032             if (!cell.initialClassName) {
19033                 cell.initialClassName = cell.dom.className;
19034             }
19035             
19036             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19037         };
19038
19039         var i = 0;
19040         
19041         for(; i < startingPos; i++) {
19042             textEls[i].innerHTML = (++prevStart);
19043             d.setDate(d.getDate()+1);
19044             
19045             cells[i].className = "fc-past fc-other-month";
19046             setCellClass(this, cells[i]);
19047         }
19048         
19049         var intDay = 0;
19050         
19051         for(; i < days; i++){
19052             intDay = i - startingPos + 1;
19053             textEls[i].innerHTML = (intDay);
19054             d.setDate(d.getDate()+1);
19055             
19056             cells[i].className = ''; // "x-date-active";
19057             setCellClass(this, cells[i]);
19058         }
19059         var extraDays = 0;
19060         
19061         for(; i < 42; i++) {
19062             textEls[i].innerHTML = (++extraDays);
19063             d.setDate(d.getDate()+1);
19064             
19065             cells[i].className = "fc-future fc-other-month";
19066             setCellClass(this, cells[i]);
19067         }
19068         
19069         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19070         
19071         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19072         
19073         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19074         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19075         
19076         if(totalRows != 6){
19077             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19078             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19079         }
19080         
19081         this.fireEvent('monthchange', this, date);
19082         
19083         
19084         /*
19085         if(!this.internalRender){
19086             var main = this.el.dom.firstChild;
19087             var w = main.offsetWidth;
19088             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19089             Roo.fly(main).setWidth(w);
19090             this.internalRender = true;
19091             // opera does not respect the auto grow header center column
19092             // then, after it gets a width opera refuses to recalculate
19093             // without a second pass
19094             if(Roo.isOpera && !this.secondPass){
19095                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19096                 this.secondPass = true;
19097                 this.update.defer(10, this, [date]);
19098             }
19099         }
19100         */
19101         
19102     },
19103     
19104     findCell : function(dt) {
19105         dt = dt.clearTime().getTime();
19106         var ret = false;
19107         this.cells.each(function(c){
19108             //Roo.log("check " +c.dateValue + '?=' + dt);
19109             if(c.dateValue == dt){
19110                 ret = c;
19111                 return false;
19112             }
19113             return true;
19114         });
19115         
19116         return ret;
19117     },
19118     
19119     findCells : function(ev) {
19120         var s = ev.start.clone().clearTime().getTime();
19121        // Roo.log(s);
19122         var e= ev.end.clone().clearTime().getTime();
19123        // Roo.log(e);
19124         var ret = [];
19125         this.cells.each(function(c){
19126              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19127             
19128             if(c.dateValue > e){
19129                 return ;
19130             }
19131             if(c.dateValue < s){
19132                 return ;
19133             }
19134             ret.push(c);
19135         });
19136         
19137         return ret;    
19138     },
19139     
19140 //    findBestRow: function(cells)
19141 //    {
19142 //        var ret = 0;
19143 //        
19144 //        for (var i =0 ; i < cells.length;i++) {
19145 //            ret  = Math.max(cells[i].rows || 0,ret);
19146 //        }
19147 //        return ret;
19148 //        
19149 //    },
19150     
19151     
19152     addItem : function(ev)
19153     {
19154         // look for vertical location slot in
19155         var cells = this.findCells(ev);
19156         
19157 //        ev.row = this.findBestRow(cells);
19158         
19159         // work out the location.
19160         
19161         var crow = false;
19162         var rows = [];
19163         for(var i =0; i < cells.length; i++) {
19164             
19165             cells[i].row = cells[0].row;
19166             
19167             if(i == 0){
19168                 cells[i].row = cells[i].row + 1;
19169             }
19170             
19171             if (!crow) {
19172                 crow = {
19173                     start : cells[i],
19174                     end :  cells[i]
19175                 };
19176                 continue;
19177             }
19178             if (crow.start.getY() == cells[i].getY()) {
19179                 // on same row.
19180                 crow.end = cells[i];
19181                 continue;
19182             }
19183             // different row.
19184             rows.push(crow);
19185             crow = {
19186                 start: cells[i],
19187                 end : cells[i]
19188             };
19189             
19190         }
19191         
19192         rows.push(crow);
19193         ev.els = [];
19194         ev.rows = rows;
19195         ev.cells = cells;
19196         
19197         cells[0].events.push(ev);
19198         
19199         this.calevents.push(ev);
19200     },
19201     
19202     clearEvents: function() {
19203         
19204         if(!this.calevents){
19205             return;
19206         }
19207         
19208         Roo.each(this.cells.elements, function(c){
19209             c.row = 0;
19210             c.events = [];
19211             c.more = [];
19212         });
19213         
19214         Roo.each(this.calevents, function(e) {
19215             Roo.each(e.els, function(el) {
19216                 el.un('mouseenter' ,this.onEventEnter, this);
19217                 el.un('mouseleave' ,this.onEventLeave, this);
19218                 el.remove();
19219             },this);
19220         },this);
19221         
19222         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19223             e.remove();
19224         });
19225         
19226     },
19227     
19228     renderEvents: function()
19229     {   
19230         var _this = this;
19231         
19232         this.cells.each(function(c) {
19233             
19234             if(c.row < 5){
19235                 return;
19236             }
19237             
19238             var ev = c.events;
19239             
19240             var r = 4;
19241             if(c.row != c.events.length){
19242                 r = 4 - (4 - (c.row - c.events.length));
19243             }
19244             
19245             c.events = ev.slice(0, r);
19246             c.more = ev.slice(r);
19247             
19248             if(c.more.length && c.more.length == 1){
19249                 c.events.push(c.more.pop());
19250             }
19251             
19252             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19253             
19254         });
19255             
19256         this.cells.each(function(c) {
19257             
19258             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19259             
19260             
19261             for (var e = 0; e < c.events.length; e++){
19262                 var ev = c.events[e];
19263                 var rows = ev.rows;
19264                 
19265                 for(var i = 0; i < rows.length; i++) {
19266                 
19267                     // how many rows should it span..
19268
19269                     var  cfg = {
19270                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19271                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19272
19273                         unselectable : "on",
19274                         cn : [
19275                             {
19276                                 cls: 'fc-event-inner',
19277                                 cn : [
19278     //                                {
19279     //                                  tag:'span',
19280     //                                  cls: 'fc-event-time',
19281     //                                  html : cells.length > 1 ? '' : ev.time
19282     //                                },
19283                                     {
19284                                       tag:'span',
19285                                       cls: 'fc-event-title',
19286                                       html : String.format('{0}', ev.title)
19287                                     }
19288
19289
19290                                 ]
19291                             },
19292                             {
19293                                 cls: 'ui-resizable-handle ui-resizable-e',
19294                                 html : '&nbsp;&nbsp;&nbsp'
19295                             }
19296
19297                         ]
19298                     };
19299
19300                     if (i == 0) {
19301                         cfg.cls += ' fc-event-start';
19302                     }
19303                     if ((i+1) == rows.length) {
19304                         cfg.cls += ' fc-event-end';
19305                     }
19306
19307                     var ctr = _this.el.select('.fc-event-container',true).first();
19308                     var cg = ctr.createChild(cfg);
19309
19310                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19311                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19312
19313                     var r = (c.more.length) ? 1 : 0;
19314                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19315                     cg.setWidth(ebox.right - sbox.x -2);
19316
19317                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19318                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19319                     cg.on('click', _this.onEventClick, _this, ev);
19320
19321                     ev.els.push(cg);
19322                     
19323                 }
19324                 
19325             }
19326             
19327             
19328             if(c.more.length){
19329                 var  cfg = {
19330                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19331                     style : 'position: absolute',
19332                     unselectable : "on",
19333                     cn : [
19334                         {
19335                             cls: 'fc-event-inner',
19336                             cn : [
19337                                 {
19338                                   tag:'span',
19339                                   cls: 'fc-event-title',
19340                                   html : 'More'
19341                                 }
19342
19343
19344                             ]
19345                         },
19346                         {
19347                             cls: 'ui-resizable-handle ui-resizable-e',
19348                             html : '&nbsp;&nbsp;&nbsp'
19349                         }
19350
19351                     ]
19352                 };
19353
19354                 var ctr = _this.el.select('.fc-event-container',true).first();
19355                 var cg = ctr.createChild(cfg);
19356
19357                 var sbox = c.select('.fc-day-content',true).first().getBox();
19358                 var ebox = c.select('.fc-day-content',true).first().getBox();
19359                 //Roo.log(cg);
19360                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19361                 cg.setWidth(ebox.right - sbox.x -2);
19362
19363                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19364                 
19365             }
19366             
19367         });
19368         
19369         
19370         
19371     },
19372     
19373     onEventEnter: function (e, el,event,d) {
19374         this.fireEvent('evententer', this, el, event);
19375     },
19376     
19377     onEventLeave: function (e, el,event,d) {
19378         this.fireEvent('eventleave', this, el, event);
19379     },
19380     
19381     onEventClick: function (e, el,event,d) {
19382         this.fireEvent('eventclick', this, el, event);
19383     },
19384     
19385     onMonthChange: function () {
19386         this.store.load();
19387     },
19388     
19389     onMoreEventClick: function(e, el, more)
19390     {
19391         var _this = this;
19392         
19393         this.calpopover.placement = 'right';
19394         this.calpopover.setTitle('More');
19395         
19396         this.calpopover.setContent('');
19397         
19398         var ctr = this.calpopover.el.select('.popover-content', true).first();
19399         
19400         Roo.each(more, function(m){
19401             var cfg = {
19402                 cls : 'fc-event-hori fc-event-draggable',
19403                 html : m.title
19404             };
19405             var cg = ctr.createChild(cfg);
19406             
19407             cg.on('click', _this.onEventClick, _this, m);
19408         });
19409         
19410         this.calpopover.show(el);
19411         
19412         
19413     },
19414     
19415     onLoad: function () 
19416     {   
19417         this.calevents = [];
19418         var cal = this;
19419         
19420         if(this.store.getCount() > 0){
19421             this.store.data.each(function(d){
19422                cal.addItem({
19423                     id : d.data.id,
19424                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19425                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19426                     time : d.data.start_time,
19427                     title : d.data.title,
19428                     description : d.data.description,
19429                     venue : d.data.venue
19430                 });
19431             });
19432         }
19433         
19434         this.renderEvents();
19435         
19436         if(this.calevents.length && this.loadMask){
19437             this.maskEl.hide();
19438         }
19439     },
19440     
19441     onBeforeLoad: function()
19442     {
19443         this.clearEvents();
19444         if(this.loadMask){
19445             this.maskEl.show();
19446         }
19447     }
19448 });
19449
19450  
19451  /*
19452  * - LGPL
19453  *
19454  * element
19455  * 
19456  */
19457
19458 /**
19459  * @class Roo.bootstrap.Popover
19460  * @extends Roo.bootstrap.Component
19461  * Bootstrap Popover class
19462  * @cfg {String} html contents of the popover   (or false to use children..)
19463  * @cfg {String} title of popover (or false to hide)
19464  * @cfg {String} placement how it is placed
19465  * @cfg {String} trigger click || hover (or false to trigger manually)
19466  * @cfg {String} over what (parent or false to trigger manually.)
19467  * @cfg {Number} delay - delay before showing
19468  
19469  * @constructor
19470  * Create a new Popover
19471  * @param {Object} config The config object
19472  */
19473
19474 Roo.bootstrap.Popover = function(config){
19475     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19476     
19477     this.addEvents({
19478         // raw events
19479          /**
19480          * @event show
19481          * After the popover show
19482          * 
19483          * @param {Roo.bootstrap.Popover} this
19484          */
19485         "show" : true,
19486         /**
19487          * @event hide
19488          * After the popover hide
19489          * 
19490          * @param {Roo.bootstrap.Popover} this
19491          */
19492         "hide" : true
19493     });
19494 };
19495
19496 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19497     
19498     title: 'Fill in a title',
19499     html: false,
19500     
19501     placement : 'right',
19502     trigger : 'hover', // hover
19503     
19504     delay : 0,
19505     
19506     over: 'parent',
19507     
19508     can_build_overlaid : false,
19509     
19510     getChildContainer : function()
19511     {
19512         return this.el.select('.popover-content',true).first();
19513     },
19514     
19515     getAutoCreate : function(){
19516          
19517         var cfg = {
19518            cls : 'popover roo-dynamic',
19519            style: 'display:block',
19520            cn : [
19521                 {
19522                     cls : 'arrow'
19523                 },
19524                 {
19525                     cls : 'popover-inner',
19526                     cn : [
19527                         {
19528                             tag: 'h3',
19529                             cls: 'popover-title popover-header',
19530                             html : this.title
19531                         },
19532                         {
19533                             cls : 'popover-content popover-body',
19534                             html : this.html
19535                         }
19536                     ]
19537                     
19538                 }
19539            ]
19540         };
19541         
19542         return cfg;
19543     },
19544     setTitle: function(str)
19545     {
19546         this.title = str;
19547         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19548     },
19549     setContent: function(str)
19550     {
19551         this.html = str;
19552         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19553     },
19554     // as it get's added to the bottom of the page.
19555     onRender : function(ct, position)
19556     {
19557         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19558         if(!this.el){
19559             var cfg = Roo.apply({},  this.getAutoCreate());
19560             cfg.id = Roo.id();
19561             
19562             if (this.cls) {
19563                 cfg.cls += ' ' + this.cls;
19564             }
19565             if (this.style) {
19566                 cfg.style = this.style;
19567             }
19568             //Roo.log("adding to ");
19569             this.el = Roo.get(document.body).createChild(cfg, position);
19570 //            Roo.log(this.el);
19571         }
19572         this.initEvents();
19573     },
19574     
19575     initEvents : function()
19576     {
19577         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19578         this.el.enableDisplayMode('block');
19579         this.el.hide();
19580         if (this.over === false) {
19581             return; 
19582         }
19583         if (this.triggers === false) {
19584             return;
19585         }
19586         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19587         var triggers = this.trigger ? this.trigger.split(' ') : [];
19588         Roo.each(triggers, function(trigger) {
19589         
19590             if (trigger == 'click') {
19591                 on_el.on('click', this.toggle, this);
19592             } else if (trigger != 'manual') {
19593                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19594                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19595       
19596                 on_el.on(eventIn  ,this.enter, this);
19597                 on_el.on(eventOut, this.leave, this);
19598             }
19599         }, this);
19600         
19601     },
19602     
19603     
19604     // private
19605     timeout : null,
19606     hoverState : null,
19607     
19608     toggle : function () {
19609         this.hoverState == 'in' ? this.leave() : this.enter();
19610     },
19611     
19612     enter : function () {
19613         
19614         clearTimeout(this.timeout);
19615     
19616         this.hoverState = 'in';
19617     
19618         if (!this.delay || !this.delay.show) {
19619             this.show();
19620             return;
19621         }
19622         var _t = this;
19623         this.timeout = setTimeout(function () {
19624             if (_t.hoverState == 'in') {
19625                 _t.show();
19626             }
19627         }, this.delay.show)
19628     },
19629     
19630     leave : function() {
19631         clearTimeout(this.timeout);
19632     
19633         this.hoverState = 'out';
19634     
19635         if (!this.delay || !this.delay.hide) {
19636             this.hide();
19637             return;
19638         }
19639         var _t = this;
19640         this.timeout = setTimeout(function () {
19641             if (_t.hoverState == 'out') {
19642                 _t.hide();
19643             }
19644         }, this.delay.hide)
19645     },
19646     
19647     show : function (on_el)
19648     {
19649         if (!on_el) {
19650             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19651         }
19652         
19653         // set content.
19654         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19655         if (this.html !== false) {
19656             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19657         }
19658         this.el.removeClass([
19659             'fade','top','bottom', 'left', 'right','in',
19660             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19661         ]);
19662         if (!this.title.length) {
19663             this.el.select('.popover-title',true).hide();
19664         }
19665         
19666         var placement = typeof this.placement == 'function' ?
19667             this.placement.call(this, this.el, on_el) :
19668             this.placement;
19669             
19670         var autoToken = /\s?auto?\s?/i;
19671         var autoPlace = autoToken.test(placement);
19672         if (autoPlace) {
19673             placement = placement.replace(autoToken, '') || 'top';
19674         }
19675         
19676         //this.el.detach()
19677         //this.el.setXY([0,0]);
19678         this.el.show();
19679         this.el.dom.style.display='block';
19680         this.el.addClass(placement);
19681         
19682         //this.el.appendTo(on_el);
19683         
19684         var p = this.getPosition();
19685         var box = this.el.getBox();
19686         
19687         if (autoPlace) {
19688             // fixme..
19689         }
19690         var align = Roo.bootstrap.Popover.alignment[placement];
19691         
19692 //        Roo.log(align);
19693         this.el.alignTo(on_el, align[0],align[1]);
19694         //var arrow = this.el.select('.arrow',true).first();
19695         //arrow.set(align[2], 
19696         
19697         this.el.addClass('in');
19698         
19699         
19700         if (this.el.hasClass('fade')) {
19701             // fade it?
19702         }
19703         
19704         this.hoverState = 'in';
19705         
19706         this.fireEvent('show', this);
19707         
19708     },
19709     hide : function()
19710     {
19711         this.el.setXY([0,0]);
19712         this.el.removeClass('in');
19713         this.el.hide();
19714         this.hoverState = null;
19715         
19716         this.fireEvent('hide', this);
19717     }
19718     
19719 });
19720
19721 Roo.bootstrap.Popover.alignment = {
19722     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19723     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19724     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19725     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19726 };
19727
19728  /*
19729  * - LGPL
19730  *
19731  * Progress
19732  * 
19733  */
19734
19735 /**
19736  * @class Roo.bootstrap.Progress
19737  * @extends Roo.bootstrap.Component
19738  * Bootstrap Progress class
19739  * @cfg {Boolean} striped striped of the progress bar
19740  * @cfg {Boolean} active animated of the progress bar
19741  * 
19742  * 
19743  * @constructor
19744  * Create a new Progress
19745  * @param {Object} config The config object
19746  */
19747
19748 Roo.bootstrap.Progress = function(config){
19749     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19750 };
19751
19752 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19753     
19754     striped : false,
19755     active: false,
19756     
19757     getAutoCreate : function(){
19758         var cfg = {
19759             tag: 'div',
19760             cls: 'progress'
19761         };
19762         
19763         
19764         if(this.striped){
19765             cfg.cls += ' progress-striped';
19766         }
19767       
19768         if(this.active){
19769             cfg.cls += ' active';
19770         }
19771         
19772         
19773         return cfg;
19774     }
19775    
19776 });
19777
19778  
19779
19780  /*
19781  * - LGPL
19782  *
19783  * ProgressBar
19784  * 
19785  */
19786
19787 /**
19788  * @class Roo.bootstrap.ProgressBar
19789  * @extends Roo.bootstrap.Component
19790  * Bootstrap ProgressBar class
19791  * @cfg {Number} aria_valuenow aria-value now
19792  * @cfg {Number} aria_valuemin aria-value min
19793  * @cfg {Number} aria_valuemax aria-value max
19794  * @cfg {String} label label for the progress bar
19795  * @cfg {String} panel (success | info | warning | danger )
19796  * @cfg {String} role role of the progress bar
19797  * @cfg {String} sr_only text
19798  * 
19799  * 
19800  * @constructor
19801  * Create a new ProgressBar
19802  * @param {Object} config The config object
19803  */
19804
19805 Roo.bootstrap.ProgressBar = function(config){
19806     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19807 };
19808
19809 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19810     
19811     aria_valuenow : 0,
19812     aria_valuemin : 0,
19813     aria_valuemax : 100,
19814     label : false,
19815     panel : false,
19816     role : false,
19817     sr_only: false,
19818     
19819     getAutoCreate : function()
19820     {
19821         
19822         var cfg = {
19823             tag: 'div',
19824             cls: 'progress-bar',
19825             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19826         };
19827         
19828         if(this.sr_only){
19829             cfg.cn = {
19830                 tag: 'span',
19831                 cls: 'sr-only',
19832                 html: this.sr_only
19833             }
19834         }
19835         
19836         if(this.role){
19837             cfg.role = this.role;
19838         }
19839         
19840         if(this.aria_valuenow){
19841             cfg['aria-valuenow'] = this.aria_valuenow;
19842         }
19843         
19844         if(this.aria_valuemin){
19845             cfg['aria-valuemin'] = this.aria_valuemin;
19846         }
19847         
19848         if(this.aria_valuemax){
19849             cfg['aria-valuemax'] = this.aria_valuemax;
19850         }
19851         
19852         if(this.label && !this.sr_only){
19853             cfg.html = this.label;
19854         }
19855         
19856         if(this.panel){
19857             cfg.cls += ' progress-bar-' + this.panel;
19858         }
19859         
19860         return cfg;
19861     },
19862     
19863     update : function(aria_valuenow)
19864     {
19865         this.aria_valuenow = aria_valuenow;
19866         
19867         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19868     }
19869    
19870 });
19871
19872  
19873
19874  /*
19875  * - LGPL
19876  *
19877  * column
19878  * 
19879  */
19880
19881 /**
19882  * @class Roo.bootstrap.TabGroup
19883  * @extends Roo.bootstrap.Column
19884  * Bootstrap Column class
19885  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19886  * @cfg {Boolean} carousel true to make the group behave like a carousel
19887  * @cfg {Boolean} bullets show bullets for the panels
19888  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19889  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19890  * @cfg {Boolean} showarrow (true|false) show arrow default true
19891  * 
19892  * @constructor
19893  * Create a new TabGroup
19894  * @param {Object} config The config object
19895  */
19896
19897 Roo.bootstrap.TabGroup = function(config){
19898     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19899     if (!this.navId) {
19900         this.navId = Roo.id();
19901     }
19902     this.tabs = [];
19903     Roo.bootstrap.TabGroup.register(this);
19904     
19905 };
19906
19907 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19908     
19909     carousel : false,
19910     transition : false,
19911     bullets : 0,
19912     timer : 0,
19913     autoslide : false,
19914     slideFn : false,
19915     slideOnTouch : false,
19916     showarrow : true,
19917     
19918     getAutoCreate : function()
19919     {
19920         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19921         
19922         cfg.cls += ' tab-content';
19923         
19924         if (this.carousel) {
19925             cfg.cls += ' carousel slide';
19926             
19927             cfg.cn = [{
19928                cls : 'carousel-inner',
19929                cn : []
19930             }];
19931         
19932             if(this.bullets  && !Roo.isTouch){
19933                 
19934                 var bullets = {
19935                     cls : 'carousel-bullets',
19936                     cn : []
19937                 };
19938                
19939                 if(this.bullets_cls){
19940                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19941                 }
19942                 
19943                 bullets.cn.push({
19944                     cls : 'clear'
19945                 });
19946                 
19947                 cfg.cn[0].cn.push(bullets);
19948             }
19949             
19950             if(this.showarrow){
19951                 cfg.cn[0].cn.push({
19952                     tag : 'div',
19953                     class : 'carousel-arrow',
19954                     cn : [
19955                         {
19956                             tag : 'div',
19957                             class : 'carousel-prev',
19958                             cn : [
19959                                 {
19960                                     tag : 'i',
19961                                     class : 'fa fa-chevron-left'
19962                                 }
19963                             ]
19964                         },
19965                         {
19966                             tag : 'div',
19967                             class : 'carousel-next',
19968                             cn : [
19969                                 {
19970                                     tag : 'i',
19971                                     class : 'fa fa-chevron-right'
19972                                 }
19973                             ]
19974                         }
19975                     ]
19976                 });
19977             }
19978             
19979         }
19980         
19981         return cfg;
19982     },
19983     
19984     initEvents:  function()
19985     {
19986 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19987 //            this.el.on("touchstart", this.onTouchStart, this);
19988 //        }
19989         
19990         if(this.autoslide){
19991             var _this = this;
19992             
19993             this.slideFn = window.setInterval(function() {
19994                 _this.showPanelNext();
19995             }, this.timer);
19996         }
19997         
19998         if(this.showarrow){
19999             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20000             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20001         }
20002         
20003         
20004     },
20005     
20006 //    onTouchStart : function(e, el, o)
20007 //    {
20008 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20009 //            return;
20010 //        }
20011 //        
20012 //        this.showPanelNext();
20013 //    },
20014     
20015     
20016     getChildContainer : function()
20017     {
20018         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20019     },
20020     
20021     /**
20022     * register a Navigation item
20023     * @param {Roo.bootstrap.NavItem} the navitem to add
20024     */
20025     register : function(item)
20026     {
20027         this.tabs.push( item);
20028         item.navId = this.navId; // not really needed..
20029         this.addBullet();
20030     
20031     },
20032     
20033     getActivePanel : function()
20034     {
20035         var r = false;
20036         Roo.each(this.tabs, function(t) {
20037             if (t.active) {
20038                 r = t;
20039                 return false;
20040             }
20041             return null;
20042         });
20043         return r;
20044         
20045     },
20046     getPanelByName : function(n)
20047     {
20048         var r = false;
20049         Roo.each(this.tabs, function(t) {
20050             if (t.tabId == n) {
20051                 r = t;
20052                 return false;
20053             }
20054             return null;
20055         });
20056         return r;
20057     },
20058     indexOfPanel : function(p)
20059     {
20060         var r = false;
20061         Roo.each(this.tabs, function(t,i) {
20062             if (t.tabId == p.tabId) {
20063                 r = i;
20064                 return false;
20065             }
20066             return null;
20067         });
20068         return r;
20069     },
20070     /**
20071      * show a specific panel
20072      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20073      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20074      */
20075     showPanel : function (pan)
20076     {
20077         if(this.transition || typeof(pan) == 'undefined'){
20078             Roo.log("waiting for the transitionend");
20079             return false;
20080         }
20081         
20082         if (typeof(pan) == 'number') {
20083             pan = this.tabs[pan];
20084         }
20085         
20086         if (typeof(pan) == 'string') {
20087             pan = this.getPanelByName(pan);
20088         }
20089         
20090         var cur = this.getActivePanel();
20091         
20092         if(!pan || !cur){
20093             Roo.log('pan or acitve pan is undefined');
20094             return false;
20095         }
20096         
20097         if (pan.tabId == this.getActivePanel().tabId) {
20098             return true;
20099         }
20100         
20101         if (false === cur.fireEvent('beforedeactivate')) {
20102             return false;
20103         }
20104         
20105         if(this.bullets > 0 && !Roo.isTouch){
20106             this.setActiveBullet(this.indexOfPanel(pan));
20107         }
20108         
20109         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20110             
20111             //class="carousel-item carousel-item-next carousel-item-left"
20112             
20113             this.transition = true;
20114             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20115             var lr = dir == 'next' ? 'left' : 'right';
20116             pan.el.addClass(dir); // or prev
20117             pan.el.addClass('carousel-item-' + dir); // or prev
20118             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20119             cur.el.addClass(lr); // or right
20120             pan.el.addClass(lr);
20121             cur.el.addClass('carousel-item-' +lr); // or right
20122             pan.el.addClass('carousel-item-' +lr);
20123             
20124             
20125             var _this = this;
20126             cur.el.on('transitionend', function() {
20127                 Roo.log("trans end?");
20128                 
20129                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20130                 pan.setActive(true);
20131                 
20132                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20133                 cur.setActive(false);
20134                 
20135                 _this.transition = false;
20136                 
20137             }, this, { single:  true } );
20138             
20139             return true;
20140         }
20141         
20142         cur.setActive(false);
20143         pan.setActive(true);
20144         
20145         return true;
20146         
20147     },
20148     showPanelNext : function()
20149     {
20150         var i = this.indexOfPanel(this.getActivePanel());
20151         
20152         if (i >= this.tabs.length - 1 && !this.autoslide) {
20153             return;
20154         }
20155         
20156         if (i >= this.tabs.length - 1 && this.autoslide) {
20157             i = -1;
20158         }
20159         
20160         this.showPanel(this.tabs[i+1]);
20161     },
20162     
20163     showPanelPrev : function()
20164     {
20165         var i = this.indexOfPanel(this.getActivePanel());
20166         
20167         if (i  < 1 && !this.autoslide) {
20168             return;
20169         }
20170         
20171         if (i < 1 && this.autoslide) {
20172             i = this.tabs.length;
20173         }
20174         
20175         this.showPanel(this.tabs[i-1]);
20176     },
20177     
20178     
20179     addBullet: function()
20180     {
20181         if(!this.bullets || Roo.isTouch){
20182             return;
20183         }
20184         var ctr = this.el.select('.carousel-bullets',true).first();
20185         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20186         var bullet = ctr.createChild({
20187             cls : 'bullet bullet-' + i
20188         },ctr.dom.lastChild);
20189         
20190         
20191         var _this = this;
20192         
20193         bullet.on('click', (function(e, el, o, ii, t){
20194
20195             e.preventDefault();
20196
20197             this.showPanel(ii);
20198
20199             if(this.autoslide && this.slideFn){
20200                 clearInterval(this.slideFn);
20201                 this.slideFn = window.setInterval(function() {
20202                     _this.showPanelNext();
20203                 }, this.timer);
20204             }
20205
20206         }).createDelegate(this, [i, bullet], true));
20207                 
20208         
20209     },
20210      
20211     setActiveBullet : function(i)
20212     {
20213         if(Roo.isTouch){
20214             return;
20215         }
20216         
20217         Roo.each(this.el.select('.bullet', true).elements, function(el){
20218             el.removeClass('selected');
20219         });
20220
20221         var bullet = this.el.select('.bullet-' + i, true).first();
20222         
20223         if(!bullet){
20224             return;
20225         }
20226         
20227         bullet.addClass('selected');
20228     }
20229     
20230     
20231   
20232 });
20233
20234  
20235
20236  
20237  
20238 Roo.apply(Roo.bootstrap.TabGroup, {
20239     
20240     groups: {},
20241      /**
20242     * register a Navigation Group
20243     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20244     */
20245     register : function(navgrp)
20246     {
20247         this.groups[navgrp.navId] = navgrp;
20248         
20249     },
20250     /**
20251     * fetch a Navigation Group based on the navigation ID
20252     * if one does not exist , it will get created.
20253     * @param {string} the navgroup to add
20254     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20255     */
20256     get: function(navId) {
20257         if (typeof(this.groups[navId]) == 'undefined') {
20258             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20259         }
20260         return this.groups[navId] ;
20261     }
20262     
20263     
20264     
20265 });
20266
20267  /*
20268  * - LGPL
20269  *
20270  * TabPanel
20271  * 
20272  */
20273
20274 /**
20275  * @class Roo.bootstrap.TabPanel
20276  * @extends Roo.bootstrap.Component
20277  * Bootstrap TabPanel class
20278  * @cfg {Boolean} active panel active
20279  * @cfg {String} html panel content
20280  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20281  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20282  * @cfg {String} href click to link..
20283  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20284  * 
20285  * 
20286  * @constructor
20287  * Create a new TabPanel
20288  * @param {Object} config The config object
20289  */
20290
20291 Roo.bootstrap.TabPanel = function(config){
20292     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20293     this.addEvents({
20294         /**
20295              * @event changed
20296              * Fires when the active status changes
20297              * @param {Roo.bootstrap.TabPanel} this
20298              * @param {Boolean} state the new state
20299             
20300          */
20301         'changed': true,
20302         /**
20303              * @event beforedeactivate
20304              * Fires before a tab is de-activated - can be used to do validation on a form.
20305              * @param {Roo.bootstrap.TabPanel} this
20306              * @return {Boolean} false if there is an error
20307             
20308          */
20309         'beforedeactivate': true
20310      });
20311     
20312     this.tabId = this.tabId || Roo.id();
20313   
20314 };
20315
20316 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20317     
20318     active: false,
20319     html: false,
20320     tabId: false,
20321     navId : false,
20322     href : '',
20323     touchSlide : false,
20324     getAutoCreate : function(){
20325         
20326         
20327         var cfg = {
20328             tag: 'div',
20329             // item is needed for carousel - not sure if it has any effect otherwise
20330             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20331             html: this.html || ''
20332         };
20333         
20334         if(this.active){
20335             cfg.cls += ' active';
20336         }
20337         
20338         if(this.tabId){
20339             cfg.tabId = this.tabId;
20340         }
20341         
20342         
20343         
20344         return cfg;
20345     },
20346     
20347     initEvents:  function()
20348     {
20349         var p = this.parent();
20350         
20351         this.navId = this.navId || p.navId;
20352         
20353         if (typeof(this.navId) != 'undefined') {
20354             // not really needed.. but just in case.. parent should be a NavGroup.
20355             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20356             
20357             tg.register(this);
20358             
20359             var i = tg.tabs.length - 1;
20360             
20361             if(this.active && tg.bullets > 0 && i < tg.bullets){
20362                 tg.setActiveBullet(i);
20363             }
20364         }
20365         
20366         this.el.on('click', this.onClick, this);
20367         
20368         if(Roo.isTouch && this.touchSlide){
20369             this.el.on("touchstart", this.onTouchStart, this);
20370             this.el.on("touchmove", this.onTouchMove, this);
20371             this.el.on("touchend", this.onTouchEnd, this);
20372         }
20373         
20374     },
20375     
20376     onRender : function(ct, position)
20377     {
20378         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20379     },
20380     
20381     setActive : function(state)
20382     {
20383         Roo.log("panel - set active " + this.tabId + "=" + state);
20384         
20385         this.active = state;
20386         if (!state) {
20387             this.el.removeClass('active');
20388             
20389         } else  if (!this.el.hasClass('active')) {
20390             this.el.addClass('active');
20391         }
20392         
20393         this.fireEvent('changed', this, state);
20394     },
20395     
20396     onClick : function(e)
20397     {
20398         e.preventDefault();
20399         
20400         if(!this.href.length){
20401             return;
20402         }
20403         
20404         window.location.href = this.href;
20405     },
20406     
20407     startX : 0,
20408     startY : 0,
20409     endX : 0,
20410     endY : 0,
20411     swiping : false,
20412     
20413     onTouchStart : function(e)
20414     {
20415         this.swiping = false;
20416         
20417         this.startX = e.browserEvent.touches[0].clientX;
20418         this.startY = e.browserEvent.touches[0].clientY;
20419     },
20420     
20421     onTouchMove : function(e)
20422     {
20423         this.swiping = true;
20424         
20425         this.endX = e.browserEvent.touches[0].clientX;
20426         this.endY = e.browserEvent.touches[0].clientY;
20427     },
20428     
20429     onTouchEnd : function(e)
20430     {
20431         if(!this.swiping){
20432             this.onClick(e);
20433             return;
20434         }
20435         
20436         var tabGroup = this.parent();
20437         
20438         if(this.endX > this.startX){ // swiping right
20439             tabGroup.showPanelPrev();
20440             return;
20441         }
20442         
20443         if(this.startX > this.endX){ // swiping left
20444             tabGroup.showPanelNext();
20445             return;
20446         }
20447     }
20448     
20449     
20450 });
20451  
20452
20453  
20454
20455  /*
20456  * - LGPL
20457  *
20458  * DateField
20459  * 
20460  */
20461
20462 /**
20463  * @class Roo.bootstrap.DateField
20464  * @extends Roo.bootstrap.Input
20465  * Bootstrap DateField class
20466  * @cfg {Number} weekStart default 0
20467  * @cfg {String} viewMode default empty, (months|years)
20468  * @cfg {String} minViewMode default empty, (months|years)
20469  * @cfg {Number} startDate default -Infinity
20470  * @cfg {Number} endDate default Infinity
20471  * @cfg {Boolean} todayHighlight default false
20472  * @cfg {Boolean} todayBtn default false
20473  * @cfg {Boolean} calendarWeeks default false
20474  * @cfg {Object} daysOfWeekDisabled default empty
20475  * @cfg {Boolean} singleMode default false (true | false)
20476  * 
20477  * @cfg {Boolean} keyboardNavigation default true
20478  * @cfg {String} language default en
20479  * 
20480  * @constructor
20481  * Create a new DateField
20482  * @param {Object} config The config object
20483  */
20484
20485 Roo.bootstrap.DateField = function(config){
20486     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20487      this.addEvents({
20488             /**
20489              * @event show
20490              * Fires when this field show.
20491              * @param {Roo.bootstrap.DateField} this
20492              * @param {Mixed} date The date value
20493              */
20494             show : true,
20495             /**
20496              * @event show
20497              * Fires when this field hide.
20498              * @param {Roo.bootstrap.DateField} this
20499              * @param {Mixed} date The date value
20500              */
20501             hide : true,
20502             /**
20503              * @event select
20504              * Fires when select a date.
20505              * @param {Roo.bootstrap.DateField} this
20506              * @param {Mixed} date The date value
20507              */
20508             select : true,
20509             /**
20510              * @event beforeselect
20511              * Fires when before select a date.
20512              * @param {Roo.bootstrap.DateField} this
20513              * @param {Mixed} date The date value
20514              */
20515             beforeselect : true
20516         });
20517 };
20518
20519 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20520     
20521     /**
20522      * @cfg {String} format
20523      * The default date format string which can be overriden for localization support.  The format must be
20524      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20525      */
20526     format : "m/d/y",
20527     /**
20528      * @cfg {String} altFormats
20529      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20530      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20531      */
20532     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20533     
20534     weekStart : 0,
20535     
20536     viewMode : '',
20537     
20538     minViewMode : '',
20539     
20540     todayHighlight : false,
20541     
20542     todayBtn: false,
20543     
20544     language: 'en',
20545     
20546     keyboardNavigation: true,
20547     
20548     calendarWeeks: false,
20549     
20550     startDate: -Infinity,
20551     
20552     endDate: Infinity,
20553     
20554     daysOfWeekDisabled: [],
20555     
20556     _events: [],
20557     
20558     singleMode : false,
20559     
20560     UTCDate: function()
20561     {
20562         return new Date(Date.UTC.apply(Date, arguments));
20563     },
20564     
20565     UTCToday: function()
20566     {
20567         var today = new Date();
20568         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20569     },
20570     
20571     getDate: function() {
20572             var d = this.getUTCDate();
20573             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20574     },
20575     
20576     getUTCDate: function() {
20577             return this.date;
20578     },
20579     
20580     setDate: function(d) {
20581             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20582     },
20583     
20584     setUTCDate: function(d) {
20585             this.date = d;
20586             this.setValue(this.formatDate(this.date));
20587     },
20588         
20589     onRender: function(ct, position)
20590     {
20591         
20592         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20593         
20594         this.language = this.language || 'en';
20595         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20596         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20597         
20598         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20599         this.format = this.format || 'm/d/y';
20600         this.isInline = false;
20601         this.isInput = true;
20602         this.component = this.el.select('.add-on', true).first() || false;
20603         this.component = (this.component && this.component.length === 0) ? false : this.component;
20604         this.hasInput = this.component && this.inputEl().length;
20605         
20606         if (typeof(this.minViewMode === 'string')) {
20607             switch (this.minViewMode) {
20608                 case 'months':
20609                     this.minViewMode = 1;
20610                     break;
20611                 case 'years':
20612                     this.minViewMode = 2;
20613                     break;
20614                 default:
20615                     this.minViewMode = 0;
20616                     break;
20617             }
20618         }
20619         
20620         if (typeof(this.viewMode === 'string')) {
20621             switch (this.viewMode) {
20622                 case 'months':
20623                     this.viewMode = 1;
20624                     break;
20625                 case 'years':
20626                     this.viewMode = 2;
20627                     break;
20628                 default:
20629                     this.viewMode = 0;
20630                     break;
20631             }
20632         }
20633                 
20634         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20635         
20636 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20637         
20638         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20639         
20640         this.picker().on('mousedown', this.onMousedown, this);
20641         this.picker().on('click', this.onClick, this);
20642         
20643         this.picker().addClass('datepicker-dropdown');
20644         
20645         this.startViewMode = this.viewMode;
20646         
20647         if(this.singleMode){
20648             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20649                 v.setVisibilityMode(Roo.Element.DISPLAY);
20650                 v.hide();
20651             });
20652             
20653             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20654                 v.setStyle('width', '189px');
20655             });
20656         }
20657         
20658         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20659             if(!this.calendarWeeks){
20660                 v.remove();
20661                 return;
20662             }
20663             
20664             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20665             v.attr('colspan', function(i, val){
20666                 return parseInt(val) + 1;
20667             });
20668         });
20669                         
20670         
20671         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20672         
20673         this.setStartDate(this.startDate);
20674         this.setEndDate(this.endDate);
20675         
20676         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20677         
20678         this.fillDow();
20679         this.fillMonths();
20680         this.update();
20681         this.showMode();
20682         
20683         if(this.isInline) {
20684             this.showPopup();
20685         }
20686     },
20687     
20688     picker : function()
20689     {
20690         return this.pickerEl;
20691 //        return this.el.select('.datepicker', true).first();
20692     },
20693     
20694     fillDow: function()
20695     {
20696         var dowCnt = this.weekStart;
20697         
20698         var dow = {
20699             tag: 'tr',
20700             cn: [
20701                 
20702             ]
20703         };
20704         
20705         if(this.calendarWeeks){
20706             dow.cn.push({
20707                 tag: 'th',
20708                 cls: 'cw',
20709                 html: '&nbsp;'
20710             })
20711         }
20712         
20713         while (dowCnt < this.weekStart + 7) {
20714             dow.cn.push({
20715                 tag: 'th',
20716                 cls: 'dow',
20717                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20718             });
20719         }
20720         
20721         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20722     },
20723     
20724     fillMonths: function()
20725     {    
20726         var i = 0;
20727         var months = this.picker().select('>.datepicker-months td', true).first();
20728         
20729         months.dom.innerHTML = '';
20730         
20731         while (i < 12) {
20732             var month = {
20733                 tag: 'span',
20734                 cls: 'month',
20735                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20736             };
20737             
20738             months.createChild(month);
20739         }
20740         
20741     },
20742     
20743     update: function()
20744     {
20745         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;
20746         
20747         if (this.date < this.startDate) {
20748             this.viewDate = new Date(this.startDate);
20749         } else if (this.date > this.endDate) {
20750             this.viewDate = new Date(this.endDate);
20751         } else {
20752             this.viewDate = new Date(this.date);
20753         }
20754         
20755         this.fill();
20756     },
20757     
20758     fill: function() 
20759     {
20760         var d = new Date(this.viewDate),
20761                 year = d.getUTCFullYear(),
20762                 month = d.getUTCMonth(),
20763                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20764                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20765                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20766                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20767                 currentDate = this.date && this.date.valueOf(),
20768                 today = this.UTCToday();
20769         
20770         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20771         
20772 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20773         
20774 //        this.picker.select('>tfoot th.today').
20775 //                                              .text(dates[this.language].today)
20776 //                                              .toggle(this.todayBtn !== false);
20777     
20778         this.updateNavArrows();
20779         this.fillMonths();
20780                                                 
20781         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20782         
20783         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20784          
20785         prevMonth.setUTCDate(day);
20786         
20787         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20788         
20789         var nextMonth = new Date(prevMonth);
20790         
20791         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20792         
20793         nextMonth = nextMonth.valueOf();
20794         
20795         var fillMonths = false;
20796         
20797         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20798         
20799         while(prevMonth.valueOf() <= nextMonth) {
20800             var clsName = '';
20801             
20802             if (prevMonth.getUTCDay() === this.weekStart) {
20803                 if(fillMonths){
20804                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20805                 }
20806                     
20807                 fillMonths = {
20808                     tag: 'tr',
20809                     cn: []
20810                 };
20811                 
20812                 if(this.calendarWeeks){
20813                     // ISO 8601: First week contains first thursday.
20814                     // ISO also states week starts on Monday, but we can be more abstract here.
20815                     var
20816                     // Start of current week: based on weekstart/current date
20817                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20818                     // Thursday of this week
20819                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20820                     // First Thursday of year, year from thursday
20821                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20822                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20823                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20824                     
20825                     fillMonths.cn.push({
20826                         tag: 'td',
20827                         cls: 'cw',
20828                         html: calWeek
20829                     });
20830                 }
20831             }
20832             
20833             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20834                 clsName += ' old';
20835             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20836                 clsName += ' new';
20837             }
20838             if (this.todayHighlight &&
20839                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20840                 prevMonth.getUTCMonth() == today.getMonth() &&
20841                 prevMonth.getUTCDate() == today.getDate()) {
20842                 clsName += ' today';
20843             }
20844             
20845             if (currentDate && prevMonth.valueOf() === currentDate) {
20846                 clsName += ' active';
20847             }
20848             
20849             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20850                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20851                     clsName += ' disabled';
20852             }
20853             
20854             fillMonths.cn.push({
20855                 tag: 'td',
20856                 cls: 'day ' + clsName,
20857                 html: prevMonth.getDate()
20858             });
20859             
20860             prevMonth.setDate(prevMonth.getDate()+1);
20861         }
20862           
20863         var currentYear = this.date && this.date.getUTCFullYear();
20864         var currentMonth = this.date && this.date.getUTCMonth();
20865         
20866         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20867         
20868         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20869             v.removeClass('active');
20870             
20871             if(currentYear === year && k === currentMonth){
20872                 v.addClass('active');
20873             }
20874             
20875             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20876                 v.addClass('disabled');
20877             }
20878             
20879         });
20880         
20881         
20882         year = parseInt(year/10, 10) * 10;
20883         
20884         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20885         
20886         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20887         
20888         year -= 1;
20889         for (var i = -1; i < 11; i++) {
20890             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20891                 tag: 'span',
20892                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20893                 html: year
20894             });
20895             
20896             year += 1;
20897         }
20898     },
20899     
20900     showMode: function(dir) 
20901     {
20902         if (dir) {
20903             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20904         }
20905         
20906         Roo.each(this.picker().select('>div',true).elements, function(v){
20907             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20908             v.hide();
20909         });
20910         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20911     },
20912     
20913     place: function()
20914     {
20915         if(this.isInline) {
20916             return;
20917         }
20918         
20919         this.picker().removeClass(['bottom', 'top']);
20920         
20921         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20922             /*
20923              * place to the top of element!
20924              *
20925              */
20926             
20927             this.picker().addClass('top');
20928             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20929             
20930             return;
20931         }
20932         
20933         this.picker().addClass('bottom');
20934         
20935         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20936     },
20937     
20938     parseDate : function(value)
20939     {
20940         if(!value || value instanceof Date){
20941             return value;
20942         }
20943         var v = Date.parseDate(value, this.format);
20944         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20945             v = Date.parseDate(value, 'Y-m-d');
20946         }
20947         if(!v && this.altFormats){
20948             if(!this.altFormatsArray){
20949                 this.altFormatsArray = this.altFormats.split("|");
20950             }
20951             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20952                 v = Date.parseDate(value, this.altFormatsArray[i]);
20953             }
20954         }
20955         return v;
20956     },
20957     
20958     formatDate : function(date, fmt)
20959     {   
20960         return (!date || !(date instanceof Date)) ?
20961         date : date.dateFormat(fmt || this.format);
20962     },
20963     
20964     onFocus : function()
20965     {
20966         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20967         this.showPopup();
20968     },
20969     
20970     onBlur : function()
20971     {
20972         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20973         
20974         var d = this.inputEl().getValue();
20975         
20976         this.setValue(d);
20977                 
20978         this.hidePopup();
20979     },
20980     
20981     showPopup : function()
20982     {
20983         this.picker().show();
20984         this.update();
20985         this.place();
20986         
20987         this.fireEvent('showpopup', this, this.date);
20988     },
20989     
20990     hidePopup : function()
20991     {
20992         if(this.isInline) {
20993             return;
20994         }
20995         this.picker().hide();
20996         this.viewMode = this.startViewMode;
20997         this.showMode();
20998         
20999         this.fireEvent('hidepopup', this, this.date);
21000         
21001     },
21002     
21003     onMousedown: function(e)
21004     {
21005         e.stopPropagation();
21006         e.preventDefault();
21007     },
21008     
21009     keyup: function(e)
21010     {
21011         Roo.bootstrap.DateField.superclass.keyup.call(this);
21012         this.update();
21013     },
21014
21015     setValue: function(v)
21016     {
21017         if(this.fireEvent('beforeselect', this, v) !== false){
21018             var d = new Date(this.parseDate(v) ).clearTime();
21019         
21020             if(isNaN(d.getTime())){
21021                 this.date = this.viewDate = '';
21022                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21023                 return;
21024             }
21025
21026             v = this.formatDate(d);
21027
21028             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21029
21030             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21031
21032             this.update();
21033
21034             this.fireEvent('select', this, this.date);
21035         }
21036     },
21037     
21038     getValue: function()
21039     {
21040         return this.formatDate(this.date);
21041     },
21042     
21043     fireKey: function(e)
21044     {
21045         if (!this.picker().isVisible()){
21046             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21047                 this.showPopup();
21048             }
21049             return;
21050         }
21051         
21052         var dateChanged = false,
21053         dir, day, month,
21054         newDate, newViewDate;
21055         
21056         switch(e.keyCode){
21057             case 27: // escape
21058                 this.hidePopup();
21059                 e.preventDefault();
21060                 break;
21061             case 37: // left
21062             case 39: // right
21063                 if (!this.keyboardNavigation) {
21064                     break;
21065                 }
21066                 dir = e.keyCode == 37 ? -1 : 1;
21067                 
21068                 if (e.ctrlKey){
21069                     newDate = this.moveYear(this.date, dir);
21070                     newViewDate = this.moveYear(this.viewDate, dir);
21071                 } else if (e.shiftKey){
21072                     newDate = this.moveMonth(this.date, dir);
21073                     newViewDate = this.moveMonth(this.viewDate, dir);
21074                 } else {
21075                     newDate = new Date(this.date);
21076                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21077                     newViewDate = new Date(this.viewDate);
21078                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21079                 }
21080                 if (this.dateWithinRange(newDate)){
21081                     this.date = newDate;
21082                     this.viewDate = newViewDate;
21083                     this.setValue(this.formatDate(this.date));
21084 //                    this.update();
21085                     e.preventDefault();
21086                     dateChanged = true;
21087                 }
21088                 break;
21089             case 38: // up
21090             case 40: // down
21091                 if (!this.keyboardNavigation) {
21092                     break;
21093                 }
21094                 dir = e.keyCode == 38 ? -1 : 1;
21095                 if (e.ctrlKey){
21096                     newDate = this.moveYear(this.date, dir);
21097                     newViewDate = this.moveYear(this.viewDate, dir);
21098                 } else if (e.shiftKey){
21099                     newDate = this.moveMonth(this.date, dir);
21100                     newViewDate = this.moveMonth(this.viewDate, dir);
21101                 } else {
21102                     newDate = new Date(this.date);
21103                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21104                     newViewDate = new Date(this.viewDate);
21105                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21106                 }
21107                 if (this.dateWithinRange(newDate)){
21108                     this.date = newDate;
21109                     this.viewDate = newViewDate;
21110                     this.setValue(this.formatDate(this.date));
21111 //                    this.update();
21112                     e.preventDefault();
21113                     dateChanged = true;
21114                 }
21115                 break;
21116             case 13: // enter
21117                 this.setValue(this.formatDate(this.date));
21118                 this.hidePopup();
21119                 e.preventDefault();
21120                 break;
21121             case 9: // tab
21122                 this.setValue(this.formatDate(this.date));
21123                 this.hidePopup();
21124                 break;
21125             case 16: // shift
21126             case 17: // ctrl
21127             case 18: // alt
21128                 break;
21129             default :
21130                 this.hidePopup();
21131                 
21132         }
21133     },
21134     
21135     
21136     onClick: function(e) 
21137     {
21138         e.stopPropagation();
21139         e.preventDefault();
21140         
21141         var target = e.getTarget();
21142         
21143         if(target.nodeName.toLowerCase() === 'i'){
21144             target = Roo.get(target).dom.parentNode;
21145         }
21146         
21147         var nodeName = target.nodeName;
21148         var className = target.className;
21149         var html = target.innerHTML;
21150         //Roo.log(nodeName);
21151         
21152         switch(nodeName.toLowerCase()) {
21153             case 'th':
21154                 switch(className) {
21155                     case 'switch':
21156                         this.showMode(1);
21157                         break;
21158                     case 'prev':
21159                     case 'next':
21160                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21161                         switch(this.viewMode){
21162                                 case 0:
21163                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21164                                         break;
21165                                 case 1:
21166                                 case 2:
21167                                         this.viewDate = this.moveYear(this.viewDate, dir);
21168                                         break;
21169                         }
21170                         this.fill();
21171                         break;
21172                     case 'today':
21173                         var date = new Date();
21174                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21175 //                        this.fill()
21176                         this.setValue(this.formatDate(this.date));
21177                         
21178                         this.hidePopup();
21179                         break;
21180                 }
21181                 break;
21182             case 'span':
21183                 if (className.indexOf('disabled') < 0) {
21184                     this.viewDate.setUTCDate(1);
21185                     if (className.indexOf('month') > -1) {
21186                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21187                     } else {
21188                         var year = parseInt(html, 10) || 0;
21189                         this.viewDate.setUTCFullYear(year);
21190                         
21191                     }
21192                     
21193                     if(this.singleMode){
21194                         this.setValue(this.formatDate(this.viewDate));
21195                         this.hidePopup();
21196                         return;
21197                     }
21198                     
21199                     this.showMode(-1);
21200                     this.fill();
21201                 }
21202                 break;
21203                 
21204             case 'td':
21205                 //Roo.log(className);
21206                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21207                     var day = parseInt(html, 10) || 1;
21208                     var year = this.viewDate.getUTCFullYear(),
21209                         month = this.viewDate.getUTCMonth();
21210
21211                     if (className.indexOf('old') > -1) {
21212                         if(month === 0 ){
21213                             month = 11;
21214                             year -= 1;
21215                         }else{
21216                             month -= 1;
21217                         }
21218                     } else if (className.indexOf('new') > -1) {
21219                         if (month == 11) {
21220                             month = 0;
21221                             year += 1;
21222                         } else {
21223                             month += 1;
21224                         }
21225                     }
21226                     //Roo.log([year,month,day]);
21227                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21228                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21229 //                    this.fill();
21230                     //Roo.log(this.formatDate(this.date));
21231                     this.setValue(this.formatDate(this.date));
21232                     this.hidePopup();
21233                 }
21234                 break;
21235         }
21236     },
21237     
21238     setStartDate: function(startDate)
21239     {
21240         this.startDate = startDate || -Infinity;
21241         if (this.startDate !== -Infinity) {
21242             this.startDate = this.parseDate(this.startDate);
21243         }
21244         this.update();
21245         this.updateNavArrows();
21246     },
21247
21248     setEndDate: function(endDate)
21249     {
21250         this.endDate = endDate || Infinity;
21251         if (this.endDate !== Infinity) {
21252             this.endDate = this.parseDate(this.endDate);
21253         }
21254         this.update();
21255         this.updateNavArrows();
21256     },
21257     
21258     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21259     {
21260         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21261         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21262             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21263         }
21264         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21265             return parseInt(d, 10);
21266         });
21267         this.update();
21268         this.updateNavArrows();
21269     },
21270     
21271     updateNavArrows: function() 
21272     {
21273         if(this.singleMode){
21274             return;
21275         }
21276         
21277         var d = new Date(this.viewDate),
21278         year = d.getUTCFullYear(),
21279         month = d.getUTCMonth();
21280         
21281         Roo.each(this.picker().select('.prev', true).elements, function(v){
21282             v.show();
21283             switch (this.viewMode) {
21284                 case 0:
21285
21286                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21287                         v.hide();
21288                     }
21289                     break;
21290                 case 1:
21291                 case 2:
21292                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21293                         v.hide();
21294                     }
21295                     break;
21296             }
21297         });
21298         
21299         Roo.each(this.picker().select('.next', true).elements, function(v){
21300             v.show();
21301             switch (this.viewMode) {
21302                 case 0:
21303
21304                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21305                         v.hide();
21306                     }
21307                     break;
21308                 case 1:
21309                 case 2:
21310                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21311                         v.hide();
21312                     }
21313                     break;
21314             }
21315         })
21316     },
21317     
21318     moveMonth: function(date, dir)
21319     {
21320         if (!dir) {
21321             return date;
21322         }
21323         var new_date = new Date(date.valueOf()),
21324         day = new_date.getUTCDate(),
21325         month = new_date.getUTCMonth(),
21326         mag = Math.abs(dir),
21327         new_month, test;
21328         dir = dir > 0 ? 1 : -1;
21329         if (mag == 1){
21330             test = dir == -1
21331             // If going back one month, make sure month is not current month
21332             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21333             ? function(){
21334                 return new_date.getUTCMonth() == month;
21335             }
21336             // If going forward one month, make sure month is as expected
21337             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21338             : function(){
21339                 return new_date.getUTCMonth() != new_month;
21340             };
21341             new_month = month + dir;
21342             new_date.setUTCMonth(new_month);
21343             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21344             if (new_month < 0 || new_month > 11) {
21345                 new_month = (new_month + 12) % 12;
21346             }
21347         } else {
21348             // For magnitudes >1, move one month at a time...
21349             for (var i=0; i<mag; i++) {
21350                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21351                 new_date = this.moveMonth(new_date, dir);
21352             }
21353             // ...then reset the day, keeping it in the new month
21354             new_month = new_date.getUTCMonth();
21355             new_date.setUTCDate(day);
21356             test = function(){
21357                 return new_month != new_date.getUTCMonth();
21358             };
21359         }
21360         // Common date-resetting loop -- if date is beyond end of month, make it
21361         // end of month
21362         while (test()){
21363             new_date.setUTCDate(--day);
21364             new_date.setUTCMonth(new_month);
21365         }
21366         return new_date;
21367     },
21368
21369     moveYear: function(date, dir)
21370     {
21371         return this.moveMonth(date, dir*12);
21372     },
21373
21374     dateWithinRange: function(date)
21375     {
21376         return date >= this.startDate && date <= this.endDate;
21377     },
21378
21379     
21380     remove: function() 
21381     {
21382         this.picker().remove();
21383     },
21384     
21385     validateValue : function(value)
21386     {
21387         if(this.getVisibilityEl().hasClass('hidden')){
21388             return true;
21389         }
21390         
21391         if(value.length < 1)  {
21392             if(this.allowBlank){
21393                 return true;
21394             }
21395             return false;
21396         }
21397         
21398         if(value.length < this.minLength){
21399             return false;
21400         }
21401         if(value.length > this.maxLength){
21402             return false;
21403         }
21404         if(this.vtype){
21405             var vt = Roo.form.VTypes;
21406             if(!vt[this.vtype](value, this)){
21407                 return false;
21408             }
21409         }
21410         if(typeof this.validator == "function"){
21411             var msg = this.validator(value);
21412             if(msg !== true){
21413                 return false;
21414             }
21415         }
21416         
21417         if(this.regex && !this.regex.test(value)){
21418             return false;
21419         }
21420         
21421         if(typeof(this.parseDate(value)) == 'undefined'){
21422             return false;
21423         }
21424         
21425         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21426             return false;
21427         }      
21428         
21429         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21430             return false;
21431         } 
21432         
21433         
21434         return true;
21435     },
21436     
21437     reset : function()
21438     {
21439         this.date = this.viewDate = '';
21440         
21441         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21442     }
21443    
21444 });
21445
21446 Roo.apply(Roo.bootstrap.DateField,  {
21447     
21448     head : {
21449         tag: 'thead',
21450         cn: [
21451         {
21452             tag: 'tr',
21453             cn: [
21454             {
21455                 tag: 'th',
21456                 cls: 'prev',
21457                 html: '<i class="fa fa-arrow-left"/>'
21458             },
21459             {
21460                 tag: 'th',
21461                 cls: 'switch',
21462                 colspan: '5'
21463             },
21464             {
21465                 tag: 'th',
21466                 cls: 'next',
21467                 html: '<i class="fa fa-arrow-right"/>'
21468             }
21469
21470             ]
21471         }
21472         ]
21473     },
21474     
21475     content : {
21476         tag: 'tbody',
21477         cn: [
21478         {
21479             tag: 'tr',
21480             cn: [
21481             {
21482                 tag: 'td',
21483                 colspan: '7'
21484             }
21485             ]
21486         }
21487         ]
21488     },
21489     
21490     footer : {
21491         tag: 'tfoot',
21492         cn: [
21493         {
21494             tag: 'tr',
21495             cn: [
21496             {
21497                 tag: 'th',
21498                 colspan: '7',
21499                 cls: 'today'
21500             }
21501                     
21502             ]
21503         }
21504         ]
21505     },
21506     
21507     dates:{
21508         en: {
21509             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21510             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21511             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21512             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21513             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21514             today: "Today"
21515         }
21516     },
21517     
21518     modes: [
21519     {
21520         clsName: 'days',
21521         navFnc: 'Month',
21522         navStep: 1
21523     },
21524     {
21525         clsName: 'months',
21526         navFnc: 'FullYear',
21527         navStep: 1
21528     },
21529     {
21530         clsName: 'years',
21531         navFnc: 'FullYear',
21532         navStep: 10
21533     }]
21534 });
21535
21536 Roo.apply(Roo.bootstrap.DateField,  {
21537   
21538     template : {
21539         tag: 'div',
21540         cls: 'datepicker dropdown-menu roo-dynamic',
21541         cn: [
21542         {
21543             tag: 'div',
21544             cls: 'datepicker-days',
21545             cn: [
21546             {
21547                 tag: 'table',
21548                 cls: 'table-condensed',
21549                 cn:[
21550                 Roo.bootstrap.DateField.head,
21551                 {
21552                     tag: 'tbody'
21553                 },
21554                 Roo.bootstrap.DateField.footer
21555                 ]
21556             }
21557             ]
21558         },
21559         {
21560             tag: 'div',
21561             cls: 'datepicker-months',
21562             cn: [
21563             {
21564                 tag: 'table',
21565                 cls: 'table-condensed',
21566                 cn:[
21567                 Roo.bootstrap.DateField.head,
21568                 Roo.bootstrap.DateField.content,
21569                 Roo.bootstrap.DateField.footer
21570                 ]
21571             }
21572             ]
21573         },
21574         {
21575             tag: 'div',
21576             cls: 'datepicker-years',
21577             cn: [
21578             {
21579                 tag: 'table',
21580                 cls: 'table-condensed',
21581                 cn:[
21582                 Roo.bootstrap.DateField.head,
21583                 Roo.bootstrap.DateField.content,
21584                 Roo.bootstrap.DateField.footer
21585                 ]
21586             }
21587             ]
21588         }
21589         ]
21590     }
21591 });
21592
21593  
21594
21595  /*
21596  * - LGPL
21597  *
21598  * TimeField
21599  * 
21600  */
21601
21602 /**
21603  * @class Roo.bootstrap.TimeField
21604  * @extends Roo.bootstrap.Input
21605  * Bootstrap DateField class
21606  * 
21607  * 
21608  * @constructor
21609  * Create a new TimeField
21610  * @param {Object} config The config object
21611  */
21612
21613 Roo.bootstrap.TimeField = function(config){
21614     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21615     this.addEvents({
21616             /**
21617              * @event show
21618              * Fires when this field show.
21619              * @param {Roo.bootstrap.DateField} thisthis
21620              * @param {Mixed} date The date value
21621              */
21622             show : true,
21623             /**
21624              * @event show
21625              * Fires when this field hide.
21626              * @param {Roo.bootstrap.DateField} this
21627              * @param {Mixed} date The date value
21628              */
21629             hide : true,
21630             /**
21631              * @event select
21632              * Fires when select a date.
21633              * @param {Roo.bootstrap.DateField} this
21634              * @param {Mixed} date The date value
21635              */
21636             select : true
21637         });
21638 };
21639
21640 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21641     
21642     /**
21643      * @cfg {String} format
21644      * The default time format string which can be overriden for localization support.  The format must be
21645      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21646      */
21647     format : "H:i",
21648        
21649     onRender: function(ct, position)
21650     {
21651         
21652         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21653                 
21654         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21655         
21656         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21657         
21658         this.pop = this.picker().select('>.datepicker-time',true).first();
21659         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21660         
21661         this.picker().on('mousedown', this.onMousedown, this);
21662         this.picker().on('click', this.onClick, this);
21663         
21664         this.picker().addClass('datepicker-dropdown');
21665     
21666         this.fillTime();
21667         this.update();
21668             
21669         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21670         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21671         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21672         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21673         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21674         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21675
21676     },
21677     
21678     fireKey: function(e){
21679         if (!this.picker().isVisible()){
21680             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21681                 this.show();
21682             }
21683             return;
21684         }
21685
21686         e.preventDefault();
21687         
21688         switch(e.keyCode){
21689             case 27: // escape
21690                 this.hide();
21691                 break;
21692             case 37: // left
21693             case 39: // right
21694                 this.onTogglePeriod();
21695                 break;
21696             case 38: // up
21697                 this.onIncrementMinutes();
21698                 break;
21699             case 40: // down
21700                 this.onDecrementMinutes();
21701                 break;
21702             case 13: // enter
21703             case 9: // tab
21704                 this.setTime();
21705                 break;
21706         }
21707     },
21708     
21709     onClick: function(e) {
21710         e.stopPropagation();
21711         e.preventDefault();
21712     },
21713     
21714     picker : function()
21715     {
21716         return this.el.select('.datepicker', true).first();
21717     },
21718     
21719     fillTime: function()
21720     {    
21721         var time = this.pop.select('tbody', true).first();
21722         
21723         time.dom.innerHTML = '';
21724         
21725         time.createChild({
21726             tag: 'tr',
21727             cn: [
21728                 {
21729                     tag: 'td',
21730                     cn: [
21731                         {
21732                             tag: 'a',
21733                             href: '#',
21734                             cls: 'btn',
21735                             cn: [
21736                                 {
21737                                     tag: 'span',
21738                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21739                                 }
21740                             ]
21741                         } 
21742                     ]
21743                 },
21744                 {
21745                     tag: 'td',
21746                     cls: 'separator'
21747                 },
21748                 {
21749                     tag: 'td',
21750                     cn: [
21751                         {
21752                             tag: 'a',
21753                             href: '#',
21754                             cls: 'btn',
21755                             cn: [
21756                                 {
21757                                     tag: 'span',
21758                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21759                                 }
21760                             ]
21761                         }
21762                     ]
21763                 },
21764                 {
21765                     tag: 'td',
21766                     cls: 'separator'
21767                 }
21768             ]
21769         });
21770         
21771         time.createChild({
21772             tag: 'tr',
21773             cn: [
21774                 {
21775                     tag: 'td',
21776                     cn: [
21777                         {
21778                             tag: 'span',
21779                             cls: 'timepicker-hour',
21780                             html: '00'
21781                         }  
21782                     ]
21783                 },
21784                 {
21785                     tag: 'td',
21786                     cls: 'separator',
21787                     html: ':'
21788                 },
21789                 {
21790                     tag: 'td',
21791                     cn: [
21792                         {
21793                             tag: 'span',
21794                             cls: 'timepicker-minute',
21795                             html: '00'
21796                         }  
21797                     ]
21798                 },
21799                 {
21800                     tag: 'td',
21801                     cls: 'separator'
21802                 },
21803                 {
21804                     tag: 'td',
21805                     cn: [
21806                         {
21807                             tag: 'button',
21808                             type: 'button',
21809                             cls: 'btn btn-primary period',
21810                             html: 'AM'
21811                             
21812                         }
21813                     ]
21814                 }
21815             ]
21816         });
21817         
21818         time.createChild({
21819             tag: 'tr',
21820             cn: [
21821                 {
21822                     tag: 'td',
21823                     cn: [
21824                         {
21825                             tag: 'a',
21826                             href: '#',
21827                             cls: 'btn',
21828                             cn: [
21829                                 {
21830                                     tag: 'span',
21831                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21832                                 }
21833                             ]
21834                         }
21835                     ]
21836                 },
21837                 {
21838                     tag: 'td',
21839                     cls: 'separator'
21840                 },
21841                 {
21842                     tag: 'td',
21843                     cn: [
21844                         {
21845                             tag: 'a',
21846                             href: '#',
21847                             cls: 'btn',
21848                             cn: [
21849                                 {
21850                                     tag: 'span',
21851                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21852                                 }
21853                             ]
21854                         }
21855                     ]
21856                 },
21857                 {
21858                     tag: 'td',
21859                     cls: 'separator'
21860                 }
21861             ]
21862         });
21863         
21864     },
21865     
21866     update: function()
21867     {
21868         
21869         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21870         
21871         this.fill();
21872     },
21873     
21874     fill: function() 
21875     {
21876         var hours = this.time.getHours();
21877         var minutes = this.time.getMinutes();
21878         var period = 'AM';
21879         
21880         if(hours > 11){
21881             period = 'PM';
21882         }
21883         
21884         if(hours == 0){
21885             hours = 12;
21886         }
21887         
21888         
21889         if(hours > 12){
21890             hours = hours - 12;
21891         }
21892         
21893         if(hours < 10){
21894             hours = '0' + hours;
21895         }
21896         
21897         if(minutes < 10){
21898             minutes = '0' + minutes;
21899         }
21900         
21901         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21902         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21903         this.pop.select('button', true).first().dom.innerHTML = period;
21904         
21905     },
21906     
21907     place: function()
21908     {   
21909         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21910         
21911         var cls = ['bottom'];
21912         
21913         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21914             cls.pop();
21915             cls.push('top');
21916         }
21917         
21918         cls.push('right');
21919         
21920         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21921             cls.pop();
21922             cls.push('left');
21923         }
21924         
21925         this.picker().addClass(cls.join('-'));
21926         
21927         var _this = this;
21928         
21929         Roo.each(cls, function(c){
21930             if(c == 'bottom'){
21931                 _this.picker().setTop(_this.inputEl().getHeight());
21932                 return;
21933             }
21934             if(c == 'top'){
21935                 _this.picker().setTop(0 - _this.picker().getHeight());
21936                 return;
21937             }
21938             
21939             if(c == 'left'){
21940                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21941                 return;
21942             }
21943             if(c == 'right'){
21944                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21945                 return;
21946             }
21947         });
21948         
21949     },
21950   
21951     onFocus : function()
21952     {
21953         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21954         this.show();
21955     },
21956     
21957     onBlur : function()
21958     {
21959         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21960         this.hide();
21961     },
21962     
21963     show : function()
21964     {
21965         this.picker().show();
21966         this.pop.show();
21967         this.update();
21968         this.place();
21969         
21970         this.fireEvent('show', this, this.date);
21971     },
21972     
21973     hide : function()
21974     {
21975         this.picker().hide();
21976         this.pop.hide();
21977         
21978         this.fireEvent('hide', this, this.date);
21979     },
21980     
21981     setTime : function()
21982     {
21983         this.hide();
21984         this.setValue(this.time.format(this.format));
21985         
21986         this.fireEvent('select', this, this.date);
21987         
21988         
21989     },
21990     
21991     onMousedown: function(e){
21992         e.stopPropagation();
21993         e.preventDefault();
21994     },
21995     
21996     onIncrementHours: function()
21997     {
21998         Roo.log('onIncrementHours');
21999         this.time = this.time.add(Date.HOUR, 1);
22000         this.update();
22001         
22002     },
22003     
22004     onDecrementHours: function()
22005     {
22006         Roo.log('onDecrementHours');
22007         this.time = this.time.add(Date.HOUR, -1);
22008         this.update();
22009     },
22010     
22011     onIncrementMinutes: function()
22012     {
22013         Roo.log('onIncrementMinutes');
22014         this.time = this.time.add(Date.MINUTE, 1);
22015         this.update();
22016     },
22017     
22018     onDecrementMinutes: function()
22019     {
22020         Roo.log('onDecrementMinutes');
22021         this.time = this.time.add(Date.MINUTE, -1);
22022         this.update();
22023     },
22024     
22025     onTogglePeriod: function()
22026     {
22027         Roo.log('onTogglePeriod');
22028         this.time = this.time.add(Date.HOUR, 12);
22029         this.update();
22030     }
22031     
22032    
22033 });
22034
22035 Roo.apply(Roo.bootstrap.TimeField,  {
22036     
22037     content : {
22038         tag: 'tbody',
22039         cn: [
22040             {
22041                 tag: 'tr',
22042                 cn: [
22043                 {
22044                     tag: 'td',
22045                     colspan: '7'
22046                 }
22047                 ]
22048             }
22049         ]
22050     },
22051     
22052     footer : {
22053         tag: 'tfoot',
22054         cn: [
22055             {
22056                 tag: 'tr',
22057                 cn: [
22058                 {
22059                     tag: 'th',
22060                     colspan: '7',
22061                     cls: '',
22062                     cn: [
22063                         {
22064                             tag: 'button',
22065                             cls: 'btn btn-info ok',
22066                             html: 'OK'
22067                         }
22068                     ]
22069                 }
22070
22071                 ]
22072             }
22073         ]
22074     }
22075 });
22076
22077 Roo.apply(Roo.bootstrap.TimeField,  {
22078   
22079     template : {
22080         tag: 'div',
22081         cls: 'datepicker dropdown-menu',
22082         cn: [
22083             {
22084                 tag: 'div',
22085                 cls: 'datepicker-time',
22086                 cn: [
22087                 {
22088                     tag: 'table',
22089                     cls: 'table-condensed',
22090                     cn:[
22091                     Roo.bootstrap.TimeField.content,
22092                     Roo.bootstrap.TimeField.footer
22093                     ]
22094                 }
22095                 ]
22096             }
22097         ]
22098     }
22099 });
22100
22101  
22102
22103  /*
22104  * - LGPL
22105  *
22106  * MonthField
22107  * 
22108  */
22109
22110 /**
22111  * @class Roo.bootstrap.MonthField
22112  * @extends Roo.bootstrap.Input
22113  * Bootstrap MonthField class
22114  * 
22115  * @cfg {String} language default en
22116  * 
22117  * @constructor
22118  * Create a new MonthField
22119  * @param {Object} config The config object
22120  */
22121
22122 Roo.bootstrap.MonthField = function(config){
22123     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22124     
22125     this.addEvents({
22126         /**
22127          * @event show
22128          * Fires when this field show.
22129          * @param {Roo.bootstrap.MonthField} this
22130          * @param {Mixed} date The date value
22131          */
22132         show : true,
22133         /**
22134          * @event show
22135          * Fires when this field hide.
22136          * @param {Roo.bootstrap.MonthField} this
22137          * @param {Mixed} date The date value
22138          */
22139         hide : true,
22140         /**
22141          * @event select
22142          * Fires when select a date.
22143          * @param {Roo.bootstrap.MonthField} this
22144          * @param {String} oldvalue The old value
22145          * @param {String} newvalue The new value
22146          */
22147         select : true
22148     });
22149 };
22150
22151 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22152     
22153     onRender: function(ct, position)
22154     {
22155         
22156         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22157         
22158         this.language = this.language || 'en';
22159         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22160         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22161         
22162         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22163         this.isInline = false;
22164         this.isInput = true;
22165         this.component = this.el.select('.add-on', true).first() || false;
22166         this.component = (this.component && this.component.length === 0) ? false : this.component;
22167         this.hasInput = this.component && this.inputEL().length;
22168         
22169         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22170         
22171         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22172         
22173         this.picker().on('mousedown', this.onMousedown, this);
22174         this.picker().on('click', this.onClick, this);
22175         
22176         this.picker().addClass('datepicker-dropdown');
22177         
22178         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22179             v.setStyle('width', '189px');
22180         });
22181         
22182         this.fillMonths();
22183         
22184         this.update();
22185         
22186         if(this.isInline) {
22187             this.show();
22188         }
22189         
22190     },
22191     
22192     setValue: function(v, suppressEvent)
22193     {   
22194         var o = this.getValue();
22195         
22196         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22197         
22198         this.update();
22199
22200         if(suppressEvent !== true){
22201             this.fireEvent('select', this, o, v);
22202         }
22203         
22204     },
22205     
22206     getValue: function()
22207     {
22208         return this.value;
22209     },
22210     
22211     onClick: function(e) 
22212     {
22213         e.stopPropagation();
22214         e.preventDefault();
22215         
22216         var target = e.getTarget();
22217         
22218         if(target.nodeName.toLowerCase() === 'i'){
22219             target = Roo.get(target).dom.parentNode;
22220         }
22221         
22222         var nodeName = target.nodeName;
22223         var className = target.className;
22224         var html = target.innerHTML;
22225         
22226         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22227             return;
22228         }
22229         
22230         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22231         
22232         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22233         
22234         this.hide();
22235                         
22236     },
22237     
22238     picker : function()
22239     {
22240         return this.pickerEl;
22241     },
22242     
22243     fillMonths: function()
22244     {    
22245         var i = 0;
22246         var months = this.picker().select('>.datepicker-months td', true).first();
22247         
22248         months.dom.innerHTML = '';
22249         
22250         while (i < 12) {
22251             var month = {
22252                 tag: 'span',
22253                 cls: 'month',
22254                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22255             };
22256             
22257             months.createChild(month);
22258         }
22259         
22260     },
22261     
22262     update: function()
22263     {
22264         var _this = this;
22265         
22266         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22267             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22268         }
22269         
22270         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22271             e.removeClass('active');
22272             
22273             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22274                 e.addClass('active');
22275             }
22276         })
22277     },
22278     
22279     place: function()
22280     {
22281         if(this.isInline) {
22282             return;
22283         }
22284         
22285         this.picker().removeClass(['bottom', 'top']);
22286         
22287         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22288             /*
22289              * place to the top of element!
22290              *
22291              */
22292             
22293             this.picker().addClass('top');
22294             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22295             
22296             return;
22297         }
22298         
22299         this.picker().addClass('bottom');
22300         
22301         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22302     },
22303     
22304     onFocus : function()
22305     {
22306         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22307         this.show();
22308     },
22309     
22310     onBlur : function()
22311     {
22312         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22313         
22314         var d = this.inputEl().getValue();
22315         
22316         this.setValue(d);
22317                 
22318         this.hide();
22319     },
22320     
22321     show : function()
22322     {
22323         this.picker().show();
22324         this.picker().select('>.datepicker-months', true).first().show();
22325         this.update();
22326         this.place();
22327         
22328         this.fireEvent('show', this, this.date);
22329     },
22330     
22331     hide : function()
22332     {
22333         if(this.isInline) {
22334             return;
22335         }
22336         this.picker().hide();
22337         this.fireEvent('hide', this, this.date);
22338         
22339     },
22340     
22341     onMousedown: function(e)
22342     {
22343         e.stopPropagation();
22344         e.preventDefault();
22345     },
22346     
22347     keyup: function(e)
22348     {
22349         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22350         this.update();
22351     },
22352
22353     fireKey: function(e)
22354     {
22355         if (!this.picker().isVisible()){
22356             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22357                 this.show();
22358             }
22359             return;
22360         }
22361         
22362         var dir;
22363         
22364         switch(e.keyCode){
22365             case 27: // escape
22366                 this.hide();
22367                 e.preventDefault();
22368                 break;
22369             case 37: // left
22370             case 39: // right
22371                 dir = e.keyCode == 37 ? -1 : 1;
22372                 
22373                 this.vIndex = this.vIndex + dir;
22374                 
22375                 if(this.vIndex < 0){
22376                     this.vIndex = 0;
22377                 }
22378                 
22379                 if(this.vIndex > 11){
22380                     this.vIndex = 11;
22381                 }
22382                 
22383                 if(isNaN(this.vIndex)){
22384                     this.vIndex = 0;
22385                 }
22386                 
22387                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22388                 
22389                 break;
22390             case 38: // up
22391             case 40: // down
22392                 
22393                 dir = e.keyCode == 38 ? -1 : 1;
22394                 
22395                 this.vIndex = this.vIndex + dir * 4;
22396                 
22397                 if(this.vIndex < 0){
22398                     this.vIndex = 0;
22399                 }
22400                 
22401                 if(this.vIndex > 11){
22402                     this.vIndex = 11;
22403                 }
22404                 
22405                 if(isNaN(this.vIndex)){
22406                     this.vIndex = 0;
22407                 }
22408                 
22409                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22410                 break;
22411                 
22412             case 13: // enter
22413                 
22414                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22415                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22416                 }
22417                 
22418                 this.hide();
22419                 e.preventDefault();
22420                 break;
22421             case 9: // tab
22422                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22423                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22424                 }
22425                 this.hide();
22426                 break;
22427             case 16: // shift
22428             case 17: // ctrl
22429             case 18: // alt
22430                 break;
22431             default :
22432                 this.hide();
22433                 
22434         }
22435     },
22436     
22437     remove: function() 
22438     {
22439         this.picker().remove();
22440     }
22441    
22442 });
22443
22444 Roo.apply(Roo.bootstrap.MonthField,  {
22445     
22446     content : {
22447         tag: 'tbody',
22448         cn: [
22449         {
22450             tag: 'tr',
22451             cn: [
22452             {
22453                 tag: 'td',
22454                 colspan: '7'
22455             }
22456             ]
22457         }
22458         ]
22459     },
22460     
22461     dates:{
22462         en: {
22463             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22464             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22465         }
22466     }
22467 });
22468
22469 Roo.apply(Roo.bootstrap.MonthField,  {
22470   
22471     template : {
22472         tag: 'div',
22473         cls: 'datepicker dropdown-menu roo-dynamic',
22474         cn: [
22475             {
22476                 tag: 'div',
22477                 cls: 'datepicker-months',
22478                 cn: [
22479                 {
22480                     tag: 'table',
22481                     cls: 'table-condensed',
22482                     cn:[
22483                         Roo.bootstrap.DateField.content
22484                     ]
22485                 }
22486                 ]
22487             }
22488         ]
22489     }
22490 });
22491
22492  
22493
22494  
22495  /*
22496  * - LGPL
22497  *
22498  * CheckBox
22499  * 
22500  */
22501
22502 /**
22503  * @class Roo.bootstrap.CheckBox
22504  * @extends Roo.bootstrap.Input
22505  * Bootstrap CheckBox class
22506  * 
22507  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22508  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22509  * @cfg {String} boxLabel The text that appears beside the checkbox
22510  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22511  * @cfg {Boolean} checked initnal the element
22512  * @cfg {Boolean} inline inline the element (default false)
22513  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22514  * @cfg {String} tooltip label tooltip
22515  * 
22516  * @constructor
22517  * Create a new CheckBox
22518  * @param {Object} config The config object
22519  */
22520
22521 Roo.bootstrap.CheckBox = function(config){
22522     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22523    
22524     this.addEvents({
22525         /**
22526         * @event check
22527         * Fires when the element is checked or unchecked.
22528         * @param {Roo.bootstrap.CheckBox} this This input
22529         * @param {Boolean} checked The new checked value
22530         */
22531        check : true,
22532        /**
22533         * @event click
22534         * Fires when the element is click.
22535         * @param {Roo.bootstrap.CheckBox} this This input
22536         */
22537        click : true
22538     });
22539     
22540 };
22541
22542 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22543   
22544     inputType: 'checkbox',
22545     inputValue: 1,
22546     valueOff: 0,
22547     boxLabel: false,
22548     checked: false,
22549     weight : false,
22550     inline: false,
22551     tooltip : '',
22552     
22553     // checkbox success does not make any sense really.. 
22554     invalidClass : "",
22555     validClass : "",
22556     
22557     
22558     getAutoCreate : function()
22559     {
22560         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22561         
22562         var id = Roo.id();
22563         
22564         var cfg = {};
22565         
22566         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22567         
22568         if(this.inline){
22569             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22570         }
22571         
22572         var input =  {
22573             tag: 'input',
22574             id : id,
22575             type : this.inputType,
22576             value : this.inputValue,
22577             cls : 'roo-' + this.inputType, //'form-box',
22578             placeholder : this.placeholder || ''
22579             
22580         };
22581         
22582         if(this.inputType != 'radio'){
22583             var hidden =  {
22584                 tag: 'input',
22585                 type : 'hidden',
22586                 cls : 'roo-hidden-value',
22587                 value : this.checked ? this.inputValue : this.valueOff
22588             };
22589         }
22590         
22591             
22592         if (this.weight) { // Validity check?
22593             cfg.cls += " " + this.inputType + "-" + this.weight;
22594         }
22595         
22596         if (this.disabled) {
22597             input.disabled=true;
22598         }
22599         
22600         if(this.checked){
22601             input.checked = this.checked;
22602         }
22603         
22604         if (this.name) {
22605             
22606             input.name = this.name;
22607             
22608             if(this.inputType != 'radio'){
22609                 hidden.name = this.name;
22610                 input.name = '_hidden_' + this.name;
22611             }
22612         }
22613         
22614         if (this.size) {
22615             input.cls += ' input-' + this.size;
22616         }
22617         
22618         var settings=this;
22619         
22620         ['xs','sm','md','lg'].map(function(size){
22621             if (settings[size]) {
22622                 cfg.cls += ' col-' + size + '-' + settings[size];
22623             }
22624         });
22625         
22626         var inputblock = input;
22627          
22628         if (this.before || this.after) {
22629             
22630             inputblock = {
22631                 cls : 'input-group',
22632                 cn :  [] 
22633             };
22634             
22635             if (this.before) {
22636                 inputblock.cn.push({
22637                     tag :'span',
22638                     cls : 'input-group-addon',
22639                     html : this.before
22640                 });
22641             }
22642             
22643             inputblock.cn.push(input);
22644             
22645             if(this.inputType != 'radio'){
22646                 inputblock.cn.push(hidden);
22647             }
22648             
22649             if (this.after) {
22650                 inputblock.cn.push({
22651                     tag :'span',
22652                     cls : 'input-group-addon',
22653                     html : this.after
22654                 });
22655             }
22656             
22657         }
22658         var boxLabelCfg = false;
22659         
22660         if(this.boxLabel){
22661            
22662             boxLabelCfg = {
22663                 tag: 'label',
22664                 //'for': id, // box label is handled by onclick - so no for...
22665                 cls: 'box-label',
22666                 html: this.boxLabel
22667             };
22668             if(this.tooltip){
22669                 boxLabelCfg.tooltip = this.tooltip;
22670             }
22671              
22672         }
22673         
22674         
22675         if (align ==='left' && this.fieldLabel.length) {
22676 //                Roo.log("left and has label");
22677             cfg.cn = [
22678                 {
22679                     tag: 'label',
22680                     'for' :  id,
22681                     cls : 'control-label',
22682                     html : this.fieldLabel
22683                 },
22684                 {
22685                     cls : "", 
22686                     cn: [
22687                         inputblock
22688                     ]
22689                 }
22690             ];
22691             
22692             if (boxLabelCfg) {
22693                 cfg.cn[1].cn.push(boxLabelCfg);
22694             }
22695             
22696             if(this.labelWidth > 12){
22697                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22698             }
22699             
22700             if(this.labelWidth < 13 && this.labelmd == 0){
22701                 this.labelmd = this.labelWidth;
22702             }
22703             
22704             if(this.labellg > 0){
22705                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22706                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22707             }
22708             
22709             if(this.labelmd > 0){
22710                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22711                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22712             }
22713             
22714             if(this.labelsm > 0){
22715                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22716                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22717             }
22718             
22719             if(this.labelxs > 0){
22720                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22721                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22722             }
22723             
22724         } else if ( this.fieldLabel.length) {
22725 //                Roo.log(" label");
22726                 cfg.cn = [
22727                    
22728                     {
22729                         tag: this.boxLabel ? 'span' : 'label',
22730                         'for': id,
22731                         cls: 'control-label box-input-label',
22732                         //cls : 'input-group-addon',
22733                         html : this.fieldLabel
22734                     },
22735                     
22736                     inputblock
22737                     
22738                 ];
22739                 if (boxLabelCfg) {
22740                     cfg.cn.push(boxLabelCfg);
22741                 }
22742
22743         } else {
22744             
22745 //                Roo.log(" no label && no align");
22746                 cfg.cn = [  inputblock ] ;
22747                 if (boxLabelCfg) {
22748                     cfg.cn.push(boxLabelCfg);
22749                 }
22750
22751                 
22752         }
22753         
22754        
22755         
22756         if(this.inputType != 'radio'){
22757             cfg.cn.push(hidden);
22758         }
22759         
22760         return cfg;
22761         
22762     },
22763     
22764     /**
22765      * return the real input element.
22766      */
22767     inputEl: function ()
22768     {
22769         return this.el.select('input.roo-' + this.inputType,true).first();
22770     },
22771     hiddenEl: function ()
22772     {
22773         return this.el.select('input.roo-hidden-value',true).first();
22774     },
22775     
22776     labelEl: function()
22777     {
22778         return this.el.select('label.control-label',true).first();
22779     },
22780     /* depricated... */
22781     
22782     label: function()
22783     {
22784         return this.labelEl();
22785     },
22786     
22787     boxLabelEl: function()
22788     {
22789         return this.el.select('label.box-label',true).first();
22790     },
22791     
22792     initEvents : function()
22793     {
22794 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22795         
22796         this.inputEl().on('click', this.onClick,  this);
22797         
22798         if (this.boxLabel) { 
22799             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22800         }
22801         
22802         this.startValue = this.getValue();
22803         
22804         if(this.groupId){
22805             Roo.bootstrap.CheckBox.register(this);
22806         }
22807     },
22808     
22809     onClick : function(e)
22810     {   
22811         if(this.fireEvent('click', this, e) !== false){
22812             this.setChecked(!this.checked);
22813         }
22814         
22815     },
22816     
22817     setChecked : function(state,suppressEvent)
22818     {
22819         this.startValue = this.getValue();
22820
22821         if(this.inputType == 'radio'){
22822             
22823             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22824                 e.dom.checked = false;
22825             });
22826             
22827             this.inputEl().dom.checked = true;
22828             
22829             this.inputEl().dom.value = this.inputValue;
22830             
22831             if(suppressEvent !== true){
22832                 this.fireEvent('check', this, true);
22833             }
22834             
22835             this.validate();
22836             
22837             return;
22838         }
22839         
22840         this.checked = state;
22841         
22842         this.inputEl().dom.checked = state;
22843         
22844         
22845         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22846         
22847         if(suppressEvent !== true){
22848             this.fireEvent('check', this, state);
22849         }
22850         
22851         this.validate();
22852     },
22853     
22854     getValue : function()
22855     {
22856         if(this.inputType == 'radio'){
22857             return this.getGroupValue();
22858         }
22859         
22860         return this.hiddenEl().dom.value;
22861         
22862     },
22863     
22864     getGroupValue : function()
22865     {
22866         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22867             return '';
22868         }
22869         
22870         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22871     },
22872     
22873     setValue : function(v,suppressEvent)
22874     {
22875         if(this.inputType == 'radio'){
22876             this.setGroupValue(v, suppressEvent);
22877             return;
22878         }
22879         
22880         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22881         
22882         this.validate();
22883     },
22884     
22885     setGroupValue : function(v, suppressEvent)
22886     {
22887         this.startValue = this.getValue();
22888         
22889         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22890             e.dom.checked = false;
22891             
22892             if(e.dom.value == v){
22893                 e.dom.checked = true;
22894             }
22895         });
22896         
22897         if(suppressEvent !== true){
22898             this.fireEvent('check', this, true);
22899         }
22900
22901         this.validate();
22902         
22903         return;
22904     },
22905     
22906     validate : function()
22907     {
22908         if(this.getVisibilityEl().hasClass('hidden')){
22909             return true;
22910         }
22911         
22912         if(
22913                 this.disabled || 
22914                 (this.inputType == 'radio' && this.validateRadio()) ||
22915                 (this.inputType == 'checkbox' && this.validateCheckbox())
22916         ){
22917             this.markValid();
22918             return true;
22919         }
22920         
22921         this.markInvalid();
22922         return false;
22923     },
22924     
22925     validateRadio : function()
22926     {
22927         if(this.getVisibilityEl().hasClass('hidden')){
22928             return true;
22929         }
22930         
22931         if(this.allowBlank){
22932             return true;
22933         }
22934         
22935         var valid = false;
22936         
22937         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22938             if(!e.dom.checked){
22939                 return;
22940             }
22941             
22942             valid = true;
22943             
22944             return false;
22945         });
22946         
22947         return valid;
22948     },
22949     
22950     validateCheckbox : function()
22951     {
22952         if(!this.groupId){
22953             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22954             //return (this.getValue() == this.inputValue) ? true : false;
22955         }
22956         
22957         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22958         
22959         if(!group){
22960             return false;
22961         }
22962         
22963         var r = false;
22964         
22965         for(var i in group){
22966             if(group[i].el.isVisible(true)){
22967                 r = false;
22968                 break;
22969             }
22970             
22971             r = true;
22972         }
22973         
22974         for(var i in group){
22975             if(r){
22976                 break;
22977             }
22978             
22979             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22980         }
22981         
22982         return r;
22983     },
22984     
22985     /**
22986      * Mark this field as valid
22987      */
22988     markValid : function()
22989     {
22990         var _this = this;
22991         
22992         this.fireEvent('valid', this);
22993         
22994         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22995         
22996         if(this.groupId){
22997             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22998         }
22999         
23000         if(label){
23001             label.markValid();
23002         }
23003
23004         if(this.inputType == 'radio'){
23005             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23006                 var fg = e.findParent('.form-group', false, true);
23007                 if (Roo.bootstrap.version == 3) {
23008                     fg.removeClass([_this.invalidClass, _this.validClass]);
23009                     fg.addClass(_this.validClass);
23010                 } else {
23011                     fg.removeClass(['is-valid', 'is-invalid']);
23012                     fg.addClass('is-valid');
23013                 }
23014             });
23015             
23016             return;
23017         }
23018
23019         if(!this.groupId){
23020             var fg = this.el.findParent('.form-group', false, true);
23021             if (Roo.bootstrap.version == 3) {
23022                 fg.removeClass([this.invalidClass, this.validClass]);
23023                 fg.addClass(this.validClass);
23024             } else {
23025                 fg.removeClass(['is-valid', 'is-invalid']);
23026                 fg.addClass('is-valid');
23027             }
23028             return;
23029         }
23030         
23031         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23032         
23033         if(!group){
23034             return;
23035         }
23036         
23037         for(var i in group){
23038             var fg = group[i].el.findParent('.form-group', false, true);
23039             if (Roo.bootstrap.version == 3) {
23040                 fg.removeClass([this.invalidClass, this.validClass]);
23041                 fg.addClass(this.validClass);
23042             } else {
23043                 fg.removeClass(['is-valid', 'is-invalid']);
23044                 fg.addClass('is-valid');
23045             }
23046         }
23047     },
23048     
23049      /**
23050      * Mark this field as invalid
23051      * @param {String} msg The validation message
23052      */
23053     markInvalid : function(msg)
23054     {
23055         if(this.allowBlank){
23056             return;
23057         }
23058         
23059         var _this = this;
23060         
23061         this.fireEvent('invalid', this, msg);
23062         
23063         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23064         
23065         if(this.groupId){
23066             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23067         }
23068         
23069         if(label){
23070             label.markInvalid();
23071         }
23072             
23073         if(this.inputType == 'radio'){
23074             
23075             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23076                 var fg = e.findParent('.form-group', false, true);
23077                 if (Roo.bootstrap.version == 3) {
23078                     fg.removeClass([_this.invalidClass, _this.validClass]);
23079                     fg.addClass(_this.invalidClass);
23080                 } else {
23081                     fg.removeClass(['is-invalid', 'is-valid']);
23082                     fg.addClass('is-invalid');
23083                 }
23084             });
23085             
23086             return;
23087         }
23088         
23089         if(!this.groupId){
23090             var fg = this.el.findParent('.form-group', false, true);
23091             if (Roo.bootstrap.version == 3) {
23092                 fg.removeClass([_this.invalidClass, _this.validClass]);
23093                 fg.addClass(_this.invalidClass);
23094             } else {
23095                 fg.removeClass(['is-invalid', 'is-valid']);
23096                 fg.addClass('is-invalid');
23097             }
23098             return;
23099         }
23100         
23101         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23102         
23103         if(!group){
23104             return;
23105         }
23106         
23107         for(var i in group){
23108             var fg = group[i].el.findParent('.form-group', false, true);
23109             if (Roo.bootstrap.version == 3) {
23110                 fg.removeClass([_this.invalidClass, _this.validClass]);
23111                 fg.addClass(_this.invalidClass);
23112             } else {
23113                 fg.removeClass(['is-invalid', 'is-valid']);
23114                 fg.addClass('is-invalid');
23115             }
23116         }
23117         
23118     },
23119     
23120     clearInvalid : function()
23121     {
23122         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23123         
23124         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23125         
23126         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23127         
23128         if (label && label.iconEl) {
23129             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23130             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23131         }
23132     },
23133     
23134     disable : function()
23135     {
23136         if(this.inputType != 'radio'){
23137             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23138             return;
23139         }
23140         
23141         var _this = this;
23142         
23143         if(this.rendered){
23144             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23145                 _this.getActionEl().addClass(this.disabledClass);
23146                 e.dom.disabled = true;
23147             });
23148         }
23149         
23150         this.disabled = true;
23151         this.fireEvent("disable", this);
23152         return this;
23153     },
23154
23155     enable : function()
23156     {
23157         if(this.inputType != 'radio'){
23158             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23159             return;
23160         }
23161         
23162         var _this = this;
23163         
23164         if(this.rendered){
23165             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23166                 _this.getActionEl().removeClass(this.disabledClass);
23167                 e.dom.disabled = false;
23168             });
23169         }
23170         
23171         this.disabled = false;
23172         this.fireEvent("enable", this);
23173         return this;
23174     },
23175     
23176     setBoxLabel : function(v)
23177     {
23178         this.boxLabel = v;
23179         
23180         if(this.rendered){
23181             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23182         }
23183     }
23184
23185 });
23186
23187 Roo.apply(Roo.bootstrap.CheckBox, {
23188     
23189     groups: {},
23190     
23191      /**
23192     * register a CheckBox Group
23193     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23194     */
23195     register : function(checkbox)
23196     {
23197         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23198             this.groups[checkbox.groupId] = {};
23199         }
23200         
23201         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23202             return;
23203         }
23204         
23205         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23206         
23207     },
23208     /**
23209     * fetch a CheckBox Group based on the group ID
23210     * @param {string} the group ID
23211     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23212     */
23213     get: function(groupId) {
23214         if (typeof(this.groups[groupId]) == 'undefined') {
23215             return false;
23216         }
23217         
23218         return this.groups[groupId] ;
23219     }
23220     
23221     
23222 });
23223 /*
23224  * - LGPL
23225  *
23226  * RadioItem
23227  * 
23228  */
23229
23230 /**
23231  * @class Roo.bootstrap.Radio
23232  * @extends Roo.bootstrap.Component
23233  * Bootstrap Radio class
23234  * @cfg {String} boxLabel - the label associated
23235  * @cfg {String} value - the value of radio
23236  * 
23237  * @constructor
23238  * Create a new Radio
23239  * @param {Object} config The config object
23240  */
23241 Roo.bootstrap.Radio = function(config){
23242     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23243     
23244 };
23245
23246 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23247     
23248     boxLabel : '',
23249     
23250     value : '',
23251     
23252     getAutoCreate : function()
23253     {
23254         var cfg = {
23255             tag : 'div',
23256             cls : 'form-group radio',
23257             cn : [
23258                 {
23259                     tag : 'label',
23260                     cls : 'box-label',
23261                     html : this.boxLabel
23262                 }
23263             ]
23264         };
23265         
23266         return cfg;
23267     },
23268     
23269     initEvents : function() 
23270     {
23271         this.parent().register(this);
23272         
23273         this.el.on('click', this.onClick, this);
23274         
23275     },
23276     
23277     onClick : function(e)
23278     {
23279         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23280             this.setChecked(true);
23281         }
23282     },
23283     
23284     setChecked : function(state, suppressEvent)
23285     {
23286         this.parent().setValue(this.value, suppressEvent);
23287         
23288     },
23289     
23290     setBoxLabel : function(v)
23291     {
23292         this.boxLabel = v;
23293         
23294         if(this.rendered){
23295             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23296         }
23297     }
23298     
23299 });
23300  
23301
23302  /*
23303  * - LGPL
23304  *
23305  * Input
23306  * 
23307  */
23308
23309 /**
23310  * @class Roo.bootstrap.SecurePass
23311  * @extends Roo.bootstrap.Input
23312  * Bootstrap SecurePass class
23313  *
23314  * 
23315  * @constructor
23316  * Create a new SecurePass
23317  * @param {Object} config The config object
23318  */
23319  
23320 Roo.bootstrap.SecurePass = function (config) {
23321     // these go here, so the translation tool can replace them..
23322     this.errors = {
23323         PwdEmpty: "Please type a password, and then retype it to confirm.",
23324         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23325         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23326         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23327         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23328         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23329         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23330         TooWeak: "Your password is Too Weak."
23331     },
23332     this.meterLabel = "Password strength:";
23333     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23334     this.meterClass = [
23335         "roo-password-meter-tooweak", 
23336         "roo-password-meter-weak", 
23337         "roo-password-meter-medium", 
23338         "roo-password-meter-strong", 
23339         "roo-password-meter-grey"
23340     ];
23341     
23342     this.errors = {};
23343     
23344     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23345 }
23346
23347 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23348     /**
23349      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23350      * {
23351      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23352      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23353      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23354      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23355      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23356      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23357      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23358      * })
23359      */
23360     // private
23361     
23362     meterWidth: 300,
23363     errorMsg :'',    
23364     errors: false,
23365     imageRoot: '/',
23366     /**
23367      * @cfg {String/Object} Label for the strength meter (defaults to
23368      * 'Password strength:')
23369      */
23370     // private
23371     meterLabel: '',
23372     /**
23373      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23374      * ['Weak', 'Medium', 'Strong'])
23375      */
23376     // private    
23377     pwdStrengths: false,    
23378     // private
23379     strength: 0,
23380     // private
23381     _lastPwd: null,
23382     // private
23383     kCapitalLetter: 0,
23384     kSmallLetter: 1,
23385     kDigit: 2,
23386     kPunctuation: 3,
23387     
23388     insecure: false,
23389     // private
23390     initEvents: function ()
23391     {
23392         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23393
23394         if (this.el.is('input[type=password]') && Roo.isSafari) {
23395             this.el.on('keydown', this.SafariOnKeyDown, this);
23396         }
23397
23398         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23399     },
23400     // private
23401     onRender: function (ct, position)
23402     {
23403         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23404         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23405         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23406
23407         this.trigger.createChild({
23408                    cn: [
23409                     {
23410                     //id: 'PwdMeter',
23411                     tag: 'div',
23412                     cls: 'roo-password-meter-grey col-xs-12',
23413                     style: {
23414                         //width: 0,
23415                         //width: this.meterWidth + 'px'                                                
23416                         }
23417                     },
23418                     {                            
23419                          cls: 'roo-password-meter-text'                          
23420                     }
23421                 ]            
23422         });
23423
23424          
23425         if (this.hideTrigger) {
23426             this.trigger.setDisplayed(false);
23427         }
23428         this.setSize(this.width || '', this.height || '');
23429     },
23430     // private
23431     onDestroy: function ()
23432     {
23433         if (this.trigger) {
23434             this.trigger.removeAllListeners();
23435             this.trigger.remove();
23436         }
23437         if (this.wrap) {
23438             this.wrap.remove();
23439         }
23440         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23441     },
23442     // private
23443     checkStrength: function ()
23444     {
23445         var pwd = this.inputEl().getValue();
23446         if (pwd == this._lastPwd) {
23447             return;
23448         }
23449
23450         var strength;
23451         if (this.ClientSideStrongPassword(pwd)) {
23452             strength = 3;
23453         } else if (this.ClientSideMediumPassword(pwd)) {
23454             strength = 2;
23455         } else if (this.ClientSideWeakPassword(pwd)) {
23456             strength = 1;
23457         } else {
23458             strength = 0;
23459         }
23460         
23461         Roo.log('strength1: ' + strength);
23462         
23463         //var pm = this.trigger.child('div/div/div').dom;
23464         var pm = this.trigger.child('div/div');
23465         pm.removeClass(this.meterClass);
23466         pm.addClass(this.meterClass[strength]);
23467                 
23468         
23469         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23470                 
23471         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23472         
23473         this._lastPwd = pwd;
23474     },
23475     reset: function ()
23476     {
23477         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23478         
23479         this._lastPwd = '';
23480         
23481         var pm = this.trigger.child('div/div');
23482         pm.removeClass(this.meterClass);
23483         pm.addClass('roo-password-meter-grey');        
23484         
23485         
23486         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23487         
23488         pt.innerHTML = '';
23489         this.inputEl().dom.type='password';
23490     },
23491     // private
23492     validateValue: function (value)
23493     {
23494         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23495             return false;
23496         }
23497         if (value.length == 0) {
23498             if (this.allowBlank) {
23499                 this.clearInvalid();
23500                 return true;
23501             }
23502
23503             this.markInvalid(this.errors.PwdEmpty);
23504             this.errorMsg = this.errors.PwdEmpty;
23505             return false;
23506         }
23507         
23508         if(this.insecure){
23509             return true;
23510         }
23511         
23512         if (!value.match(/[\x21-\x7e]+/)) {
23513             this.markInvalid(this.errors.PwdBadChar);
23514             this.errorMsg = this.errors.PwdBadChar;
23515             return false;
23516         }
23517         if (value.length < 6) {
23518             this.markInvalid(this.errors.PwdShort);
23519             this.errorMsg = this.errors.PwdShort;
23520             return false;
23521         }
23522         if (value.length > 16) {
23523             this.markInvalid(this.errors.PwdLong);
23524             this.errorMsg = this.errors.PwdLong;
23525             return false;
23526         }
23527         var strength;
23528         if (this.ClientSideStrongPassword(value)) {
23529             strength = 3;
23530         } else if (this.ClientSideMediumPassword(value)) {
23531             strength = 2;
23532         } else if (this.ClientSideWeakPassword(value)) {
23533             strength = 1;
23534         } else {
23535             strength = 0;
23536         }
23537
23538         
23539         if (strength < 2) {
23540             //this.markInvalid(this.errors.TooWeak);
23541             this.errorMsg = this.errors.TooWeak;
23542             //return false;
23543         }
23544         
23545         
23546         console.log('strength2: ' + strength);
23547         
23548         //var pm = this.trigger.child('div/div/div').dom;
23549         
23550         var pm = this.trigger.child('div/div');
23551         pm.removeClass(this.meterClass);
23552         pm.addClass(this.meterClass[strength]);
23553                 
23554         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23555                 
23556         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23557         
23558         this.errorMsg = ''; 
23559         return true;
23560     },
23561     // private
23562     CharacterSetChecks: function (type)
23563     {
23564         this.type = type;
23565         this.fResult = false;
23566     },
23567     // private
23568     isctype: function (character, type)
23569     {
23570         switch (type) {  
23571             case this.kCapitalLetter:
23572                 if (character >= 'A' && character <= 'Z') {
23573                     return true;
23574                 }
23575                 break;
23576             
23577             case this.kSmallLetter:
23578                 if (character >= 'a' && character <= 'z') {
23579                     return true;
23580                 }
23581                 break;
23582             
23583             case this.kDigit:
23584                 if (character >= '0' && character <= '9') {
23585                     return true;
23586                 }
23587                 break;
23588             
23589             case this.kPunctuation:
23590                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23591                     return true;
23592                 }
23593                 break;
23594             
23595             default:
23596                 return false;
23597         }
23598
23599     },
23600     // private
23601     IsLongEnough: function (pwd, size)
23602     {
23603         return !(pwd == null || isNaN(size) || pwd.length < size);
23604     },
23605     // private
23606     SpansEnoughCharacterSets: function (word, nb)
23607     {
23608         if (!this.IsLongEnough(word, nb))
23609         {
23610             return false;
23611         }
23612
23613         var characterSetChecks = new Array(
23614             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23615             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23616         );
23617         
23618         for (var index = 0; index < word.length; ++index) {
23619             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23620                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23621                     characterSetChecks[nCharSet].fResult = true;
23622                     break;
23623                 }
23624             }
23625         }
23626
23627         var nCharSets = 0;
23628         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23629             if (characterSetChecks[nCharSet].fResult) {
23630                 ++nCharSets;
23631             }
23632         }
23633
23634         if (nCharSets < nb) {
23635             return false;
23636         }
23637         return true;
23638     },
23639     // private
23640     ClientSideStrongPassword: function (pwd)
23641     {
23642         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23643     },
23644     // private
23645     ClientSideMediumPassword: function (pwd)
23646     {
23647         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23648     },
23649     // private
23650     ClientSideWeakPassword: function (pwd)
23651     {
23652         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23653     }
23654           
23655 })//<script type="text/javascript">
23656
23657 /*
23658  * Based  Ext JS Library 1.1.1
23659  * Copyright(c) 2006-2007, Ext JS, LLC.
23660  * LGPL
23661  *
23662  */
23663  
23664 /**
23665  * @class Roo.HtmlEditorCore
23666  * @extends Roo.Component
23667  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23668  *
23669  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23670  */
23671
23672 Roo.HtmlEditorCore = function(config){
23673     
23674     
23675     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23676     
23677     
23678     this.addEvents({
23679         /**
23680          * @event initialize
23681          * Fires when the editor is fully initialized (including the iframe)
23682          * @param {Roo.HtmlEditorCore} this
23683          */
23684         initialize: true,
23685         /**
23686          * @event activate
23687          * Fires when the editor is first receives the focus. Any insertion must wait
23688          * until after this event.
23689          * @param {Roo.HtmlEditorCore} this
23690          */
23691         activate: true,
23692          /**
23693          * @event beforesync
23694          * Fires before the textarea is updated with content from the editor iframe. Return false
23695          * to cancel the sync.
23696          * @param {Roo.HtmlEditorCore} this
23697          * @param {String} html
23698          */
23699         beforesync: true,
23700          /**
23701          * @event beforepush
23702          * Fires before the iframe editor is updated with content from the textarea. Return false
23703          * to cancel the push.
23704          * @param {Roo.HtmlEditorCore} this
23705          * @param {String} html
23706          */
23707         beforepush: true,
23708          /**
23709          * @event sync
23710          * Fires when the textarea is updated with content from the editor iframe.
23711          * @param {Roo.HtmlEditorCore} this
23712          * @param {String} html
23713          */
23714         sync: true,
23715          /**
23716          * @event push
23717          * Fires when the iframe editor is updated with content from the textarea.
23718          * @param {Roo.HtmlEditorCore} this
23719          * @param {String} html
23720          */
23721         push: true,
23722         
23723         /**
23724          * @event editorevent
23725          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23726          * @param {Roo.HtmlEditorCore} this
23727          */
23728         editorevent: true
23729         
23730     });
23731     
23732     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23733     
23734     // defaults : white / black...
23735     this.applyBlacklists();
23736     
23737     
23738     
23739 };
23740
23741
23742 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23743
23744
23745      /**
23746      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23747      */
23748     
23749     owner : false,
23750     
23751      /**
23752      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23753      *                        Roo.resizable.
23754      */
23755     resizable : false,
23756      /**
23757      * @cfg {Number} height (in pixels)
23758      */   
23759     height: 300,
23760    /**
23761      * @cfg {Number} width (in pixels)
23762      */   
23763     width: 500,
23764     
23765     /**
23766      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23767      * 
23768      */
23769     stylesheets: false,
23770     
23771     // id of frame..
23772     frameId: false,
23773     
23774     // private properties
23775     validationEvent : false,
23776     deferHeight: true,
23777     initialized : false,
23778     activated : false,
23779     sourceEditMode : false,
23780     onFocus : Roo.emptyFn,
23781     iframePad:3,
23782     hideMode:'offsets',
23783     
23784     clearUp: true,
23785     
23786     // blacklist + whitelisted elements..
23787     black: false,
23788     white: false,
23789      
23790     bodyCls : '',
23791
23792     /**
23793      * Protected method that will not generally be called directly. It
23794      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23795      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23796      */
23797     getDocMarkup : function(){
23798         // body styles..
23799         var st = '';
23800         
23801         // inherit styels from page...?? 
23802         if (this.stylesheets === false) {
23803             
23804             Roo.get(document.head).select('style').each(function(node) {
23805                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23806             });
23807             
23808             Roo.get(document.head).select('link').each(function(node) { 
23809                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23810             });
23811             
23812         } else if (!this.stylesheets.length) {
23813                 // simple..
23814                 st = '<style type="text/css">' +
23815                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23816                    '</style>';
23817         } else {
23818             for (var i in this.stylesheets) { 
23819                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23820             }
23821             
23822         }
23823         
23824         st +=  '<style type="text/css">' +
23825             'IMG { cursor: pointer } ' +
23826         '</style>';
23827
23828         var cls = 'roo-htmleditor-body';
23829         
23830         if(this.bodyCls.length){
23831             cls += ' ' + this.bodyCls;
23832         }
23833         
23834         return '<html><head>' + st  +
23835             //<style type="text/css">' +
23836             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23837             //'</style>' +
23838             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23839     },
23840
23841     // private
23842     onRender : function(ct, position)
23843     {
23844         var _t = this;
23845         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23846         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23847         
23848         
23849         this.el.dom.style.border = '0 none';
23850         this.el.dom.setAttribute('tabIndex', -1);
23851         this.el.addClass('x-hidden hide');
23852         
23853         
23854         
23855         if(Roo.isIE){ // fix IE 1px bogus margin
23856             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23857         }
23858        
23859         
23860         this.frameId = Roo.id();
23861         
23862          
23863         
23864         var iframe = this.owner.wrap.createChild({
23865             tag: 'iframe',
23866             cls: 'form-control', // bootstrap..
23867             id: this.frameId,
23868             name: this.frameId,
23869             frameBorder : 'no',
23870             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23871         }, this.el
23872         );
23873         
23874         
23875         this.iframe = iframe.dom;
23876
23877          this.assignDocWin();
23878         
23879         this.doc.designMode = 'on';
23880        
23881         this.doc.open();
23882         this.doc.write(this.getDocMarkup());
23883         this.doc.close();
23884
23885         
23886         var task = { // must defer to wait for browser to be ready
23887             run : function(){
23888                 //console.log("run task?" + this.doc.readyState);
23889                 this.assignDocWin();
23890                 if(this.doc.body || this.doc.readyState == 'complete'){
23891                     try {
23892                         this.doc.designMode="on";
23893                     } catch (e) {
23894                         return;
23895                     }
23896                     Roo.TaskMgr.stop(task);
23897                     this.initEditor.defer(10, this);
23898                 }
23899             },
23900             interval : 10,
23901             duration: 10000,
23902             scope: this
23903         };
23904         Roo.TaskMgr.start(task);
23905
23906     },
23907
23908     // private
23909     onResize : function(w, h)
23910     {
23911          Roo.log('resize: ' +w + ',' + h );
23912         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23913         if(!this.iframe){
23914             return;
23915         }
23916         if(typeof w == 'number'){
23917             
23918             this.iframe.style.width = w + 'px';
23919         }
23920         if(typeof h == 'number'){
23921             
23922             this.iframe.style.height = h + 'px';
23923             if(this.doc){
23924                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23925             }
23926         }
23927         
23928     },
23929
23930     /**
23931      * Toggles the editor between standard and source edit mode.
23932      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23933      */
23934     toggleSourceEdit : function(sourceEditMode){
23935         
23936         this.sourceEditMode = sourceEditMode === true;
23937         
23938         if(this.sourceEditMode){
23939  
23940             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23941             
23942         }else{
23943             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23944             //this.iframe.className = '';
23945             this.deferFocus();
23946         }
23947         //this.setSize(this.owner.wrap.getSize());
23948         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23949     },
23950
23951     
23952   
23953
23954     /**
23955      * Protected method that will not generally be called directly. If you need/want
23956      * custom HTML cleanup, this is the method you should override.
23957      * @param {String} html The HTML to be cleaned
23958      * return {String} The cleaned HTML
23959      */
23960     cleanHtml : function(html){
23961         html = String(html);
23962         if(html.length > 5){
23963             if(Roo.isSafari){ // strip safari nonsense
23964                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23965             }
23966         }
23967         if(html == '&nbsp;'){
23968             html = '';
23969         }
23970         return html;
23971     },
23972
23973     /**
23974      * HTML Editor -> Textarea
23975      * Protected method that will not generally be called directly. Syncs the contents
23976      * of the editor iframe with the textarea.
23977      */
23978     syncValue : function(){
23979         if(this.initialized){
23980             var bd = (this.doc.body || this.doc.documentElement);
23981             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23982             var html = bd.innerHTML;
23983             if(Roo.isSafari){
23984                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23985                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23986                 if(m && m[1]){
23987                     html = '<div style="'+m[0]+'">' + html + '</div>';
23988                 }
23989             }
23990             html = this.cleanHtml(html);
23991             // fix up the special chars.. normaly like back quotes in word...
23992             // however we do not want to do this with chinese..
23993             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23994                 
23995                 var cc = match.charCodeAt();
23996
23997                 // Get the character value, handling surrogate pairs
23998                 if (match.length == 2) {
23999                     // It's a surrogate pair, calculate the Unicode code point
24000                     var high = match.charCodeAt(0) - 0xD800;
24001                     var low  = match.charCodeAt(1) - 0xDC00;
24002                     cc = (high * 0x400) + low + 0x10000;
24003                 }  else if (
24004                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24005                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24006                     (cc >= 0xf900 && cc < 0xfb00 )
24007                 ) {
24008                         return match;
24009                 }  
24010          
24011                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24012                 return "&#" + cc + ";";
24013                 
24014                 
24015             });
24016             
24017             
24018              
24019             if(this.owner.fireEvent('beforesync', this, html) !== false){
24020                 this.el.dom.value = html;
24021                 this.owner.fireEvent('sync', this, html);
24022             }
24023         }
24024     },
24025
24026     /**
24027      * Protected method that will not generally be called directly. Pushes the value of the textarea
24028      * into the iframe editor.
24029      */
24030     pushValue : function(){
24031         if(this.initialized){
24032             var v = this.el.dom.value.trim();
24033             
24034 //            if(v.length < 1){
24035 //                v = '&#160;';
24036 //            }
24037             
24038             if(this.owner.fireEvent('beforepush', this, v) !== false){
24039                 var d = (this.doc.body || this.doc.documentElement);
24040                 d.innerHTML = v;
24041                 this.cleanUpPaste();
24042                 this.el.dom.value = d.innerHTML;
24043                 this.owner.fireEvent('push', this, v);
24044             }
24045         }
24046     },
24047
24048     // private
24049     deferFocus : function(){
24050         this.focus.defer(10, this);
24051     },
24052
24053     // doc'ed in Field
24054     focus : function(){
24055         if(this.win && !this.sourceEditMode){
24056             this.win.focus();
24057         }else{
24058             this.el.focus();
24059         }
24060     },
24061     
24062     assignDocWin: function()
24063     {
24064         var iframe = this.iframe;
24065         
24066          if(Roo.isIE){
24067             this.doc = iframe.contentWindow.document;
24068             this.win = iframe.contentWindow;
24069         } else {
24070 //            if (!Roo.get(this.frameId)) {
24071 //                return;
24072 //            }
24073 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24074 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24075             
24076             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24077                 return;
24078             }
24079             
24080             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24081             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24082         }
24083     },
24084     
24085     // private
24086     initEditor : function(){
24087         //console.log("INIT EDITOR");
24088         this.assignDocWin();
24089         
24090         
24091         
24092         this.doc.designMode="on";
24093         this.doc.open();
24094         this.doc.write(this.getDocMarkup());
24095         this.doc.close();
24096         
24097         var dbody = (this.doc.body || this.doc.documentElement);
24098         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24099         // this copies styles from the containing element into thsi one..
24100         // not sure why we need all of this..
24101         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24102         
24103         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24104         //ss['background-attachment'] = 'fixed'; // w3c
24105         dbody.bgProperties = 'fixed'; // ie
24106         //Roo.DomHelper.applyStyles(dbody, ss);
24107         Roo.EventManager.on(this.doc, {
24108             //'mousedown': this.onEditorEvent,
24109             'mouseup': this.onEditorEvent,
24110             'dblclick': this.onEditorEvent,
24111             'click': this.onEditorEvent,
24112             'keyup': this.onEditorEvent,
24113             buffer:100,
24114             scope: this
24115         });
24116         if(Roo.isGecko){
24117             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24118         }
24119         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24120             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24121         }
24122         this.initialized = true;
24123
24124         this.owner.fireEvent('initialize', this);
24125         this.pushValue();
24126     },
24127
24128     // private
24129     onDestroy : function(){
24130         
24131         
24132         
24133         if(this.rendered){
24134             
24135             //for (var i =0; i < this.toolbars.length;i++) {
24136             //    // fixme - ask toolbars for heights?
24137             //    this.toolbars[i].onDestroy();
24138            // }
24139             
24140             //this.wrap.dom.innerHTML = '';
24141             //this.wrap.remove();
24142         }
24143     },
24144
24145     // private
24146     onFirstFocus : function(){
24147         
24148         this.assignDocWin();
24149         
24150         
24151         this.activated = true;
24152          
24153     
24154         if(Roo.isGecko){ // prevent silly gecko errors
24155             this.win.focus();
24156             var s = this.win.getSelection();
24157             if(!s.focusNode || s.focusNode.nodeType != 3){
24158                 var r = s.getRangeAt(0);
24159                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24160                 r.collapse(true);
24161                 this.deferFocus();
24162             }
24163             try{
24164                 this.execCmd('useCSS', true);
24165                 this.execCmd('styleWithCSS', false);
24166             }catch(e){}
24167         }
24168         this.owner.fireEvent('activate', this);
24169     },
24170
24171     // private
24172     adjustFont: function(btn){
24173         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24174         //if(Roo.isSafari){ // safari
24175         //    adjust *= 2;
24176        // }
24177         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24178         if(Roo.isSafari){ // safari
24179             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24180             v =  (v < 10) ? 10 : v;
24181             v =  (v > 48) ? 48 : v;
24182             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24183             
24184         }
24185         
24186         
24187         v = Math.max(1, v+adjust);
24188         
24189         this.execCmd('FontSize', v  );
24190     },
24191
24192     onEditorEvent : function(e)
24193     {
24194         this.owner.fireEvent('editorevent', this, e);
24195       //  this.updateToolbar();
24196         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24197     },
24198
24199     insertTag : function(tg)
24200     {
24201         // could be a bit smarter... -> wrap the current selected tRoo..
24202         if (tg.toLowerCase() == 'span' ||
24203             tg.toLowerCase() == 'code' ||
24204             tg.toLowerCase() == 'sup' ||
24205             tg.toLowerCase() == 'sub' 
24206             ) {
24207             
24208             range = this.createRange(this.getSelection());
24209             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24210             wrappingNode.appendChild(range.extractContents());
24211             range.insertNode(wrappingNode);
24212
24213             return;
24214             
24215             
24216             
24217         }
24218         this.execCmd("formatblock",   tg);
24219         
24220     },
24221     
24222     insertText : function(txt)
24223     {
24224         
24225         
24226         var range = this.createRange();
24227         range.deleteContents();
24228                //alert(Sender.getAttribute('label'));
24229                
24230         range.insertNode(this.doc.createTextNode(txt));
24231     } ,
24232     
24233      
24234
24235     /**
24236      * Executes a Midas editor command on the editor document and performs necessary focus and
24237      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24238      * @param {String} cmd The Midas command
24239      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24240      */
24241     relayCmd : function(cmd, value){
24242         this.win.focus();
24243         this.execCmd(cmd, value);
24244         this.owner.fireEvent('editorevent', this);
24245         //this.updateToolbar();
24246         this.owner.deferFocus();
24247     },
24248
24249     /**
24250      * Executes a Midas editor command directly on the editor document.
24251      * For visual commands, you should use {@link #relayCmd} instead.
24252      * <b>This should only be called after the editor is initialized.</b>
24253      * @param {String} cmd The Midas command
24254      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24255      */
24256     execCmd : function(cmd, value){
24257         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24258         this.syncValue();
24259     },
24260  
24261  
24262    
24263     /**
24264      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24265      * to insert tRoo.
24266      * @param {String} text | dom node.. 
24267      */
24268     insertAtCursor : function(text)
24269     {
24270         
24271         if(!this.activated){
24272             return;
24273         }
24274         /*
24275         if(Roo.isIE){
24276             this.win.focus();
24277             var r = this.doc.selection.createRange();
24278             if(r){
24279                 r.collapse(true);
24280                 r.pasteHTML(text);
24281                 this.syncValue();
24282                 this.deferFocus();
24283             
24284             }
24285             return;
24286         }
24287         */
24288         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24289             this.win.focus();
24290             
24291             
24292             // from jquery ui (MIT licenced)
24293             var range, node;
24294             var win = this.win;
24295             
24296             if (win.getSelection && win.getSelection().getRangeAt) {
24297                 range = win.getSelection().getRangeAt(0);
24298                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24299                 range.insertNode(node);
24300             } else if (win.document.selection && win.document.selection.createRange) {
24301                 // no firefox support
24302                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24303                 win.document.selection.createRange().pasteHTML(txt);
24304             } else {
24305                 // no firefox support
24306                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24307                 this.execCmd('InsertHTML', txt);
24308             } 
24309             
24310             this.syncValue();
24311             
24312             this.deferFocus();
24313         }
24314     },
24315  // private
24316     mozKeyPress : function(e){
24317         if(e.ctrlKey){
24318             var c = e.getCharCode(), cmd;
24319           
24320             if(c > 0){
24321                 c = String.fromCharCode(c).toLowerCase();
24322                 switch(c){
24323                     case 'b':
24324                         cmd = 'bold';
24325                         break;
24326                     case 'i':
24327                         cmd = 'italic';
24328                         break;
24329                     
24330                     case 'u':
24331                         cmd = 'underline';
24332                         break;
24333                     
24334                     case 'v':
24335                         this.cleanUpPaste.defer(100, this);
24336                         return;
24337                         
24338                 }
24339                 if(cmd){
24340                     this.win.focus();
24341                     this.execCmd(cmd);
24342                     this.deferFocus();
24343                     e.preventDefault();
24344                 }
24345                 
24346             }
24347         }
24348     },
24349
24350     // private
24351     fixKeys : function(){ // load time branching for fastest keydown performance
24352         if(Roo.isIE){
24353             return function(e){
24354                 var k = e.getKey(), r;
24355                 if(k == e.TAB){
24356                     e.stopEvent();
24357                     r = this.doc.selection.createRange();
24358                     if(r){
24359                         r.collapse(true);
24360                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24361                         this.deferFocus();
24362                     }
24363                     return;
24364                 }
24365                 
24366                 if(k == e.ENTER){
24367                     r = this.doc.selection.createRange();
24368                     if(r){
24369                         var target = r.parentElement();
24370                         if(!target || target.tagName.toLowerCase() != 'li'){
24371                             e.stopEvent();
24372                             r.pasteHTML('<br />');
24373                             r.collapse(false);
24374                             r.select();
24375                         }
24376                     }
24377                 }
24378                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24379                     this.cleanUpPaste.defer(100, this);
24380                     return;
24381                 }
24382                 
24383                 
24384             };
24385         }else if(Roo.isOpera){
24386             return function(e){
24387                 var k = e.getKey();
24388                 if(k == e.TAB){
24389                     e.stopEvent();
24390                     this.win.focus();
24391                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24392                     this.deferFocus();
24393                 }
24394                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24395                     this.cleanUpPaste.defer(100, this);
24396                     return;
24397                 }
24398                 
24399             };
24400         }else if(Roo.isSafari){
24401             return function(e){
24402                 var k = e.getKey();
24403                 
24404                 if(k == e.TAB){
24405                     e.stopEvent();
24406                     this.execCmd('InsertText','\t');
24407                     this.deferFocus();
24408                     return;
24409                 }
24410                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24411                     this.cleanUpPaste.defer(100, this);
24412                     return;
24413                 }
24414                 
24415              };
24416         }
24417     }(),
24418     
24419     getAllAncestors: function()
24420     {
24421         var p = this.getSelectedNode();
24422         var a = [];
24423         if (!p) {
24424             a.push(p); // push blank onto stack..
24425             p = this.getParentElement();
24426         }
24427         
24428         
24429         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24430             a.push(p);
24431             p = p.parentNode;
24432         }
24433         a.push(this.doc.body);
24434         return a;
24435     },
24436     lastSel : false,
24437     lastSelNode : false,
24438     
24439     
24440     getSelection : function() 
24441     {
24442         this.assignDocWin();
24443         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24444     },
24445     
24446     getSelectedNode: function() 
24447     {
24448         // this may only work on Gecko!!!
24449         
24450         // should we cache this!!!!
24451         
24452         
24453         
24454          
24455         var range = this.createRange(this.getSelection()).cloneRange();
24456         
24457         if (Roo.isIE) {
24458             var parent = range.parentElement();
24459             while (true) {
24460                 var testRange = range.duplicate();
24461                 testRange.moveToElementText(parent);
24462                 if (testRange.inRange(range)) {
24463                     break;
24464                 }
24465                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24466                     break;
24467                 }
24468                 parent = parent.parentElement;
24469             }
24470             return parent;
24471         }
24472         
24473         // is ancestor a text element.
24474         var ac =  range.commonAncestorContainer;
24475         if (ac.nodeType == 3) {
24476             ac = ac.parentNode;
24477         }
24478         
24479         var ar = ac.childNodes;
24480          
24481         var nodes = [];
24482         var other_nodes = [];
24483         var has_other_nodes = false;
24484         for (var i=0;i<ar.length;i++) {
24485             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24486                 continue;
24487             }
24488             // fullly contained node.
24489             
24490             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24491                 nodes.push(ar[i]);
24492                 continue;
24493             }
24494             
24495             // probably selected..
24496             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24497                 other_nodes.push(ar[i]);
24498                 continue;
24499             }
24500             // outer..
24501             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24502                 continue;
24503             }
24504             
24505             
24506             has_other_nodes = true;
24507         }
24508         if (!nodes.length && other_nodes.length) {
24509             nodes= other_nodes;
24510         }
24511         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24512             return false;
24513         }
24514         
24515         return nodes[0];
24516     },
24517     createRange: function(sel)
24518     {
24519         // this has strange effects when using with 
24520         // top toolbar - not sure if it's a great idea.
24521         //this.editor.contentWindow.focus();
24522         if (typeof sel != "undefined") {
24523             try {
24524                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24525             } catch(e) {
24526                 return this.doc.createRange();
24527             }
24528         } else {
24529             return this.doc.createRange();
24530         }
24531     },
24532     getParentElement: function()
24533     {
24534         
24535         this.assignDocWin();
24536         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24537         
24538         var range = this.createRange(sel);
24539          
24540         try {
24541             var p = range.commonAncestorContainer;
24542             while (p.nodeType == 3) { // text node
24543                 p = p.parentNode;
24544             }
24545             return p;
24546         } catch (e) {
24547             return null;
24548         }
24549     
24550     },
24551     /***
24552      *
24553      * Range intersection.. the hard stuff...
24554      *  '-1' = before
24555      *  '0' = hits..
24556      *  '1' = after.
24557      *         [ -- selected range --- ]
24558      *   [fail]                        [fail]
24559      *
24560      *    basically..
24561      *      if end is before start or  hits it. fail.
24562      *      if start is after end or hits it fail.
24563      *
24564      *   if either hits (but other is outside. - then it's not 
24565      *   
24566      *    
24567      **/
24568     
24569     
24570     // @see http://www.thismuchiknow.co.uk/?p=64.
24571     rangeIntersectsNode : function(range, node)
24572     {
24573         var nodeRange = node.ownerDocument.createRange();
24574         try {
24575             nodeRange.selectNode(node);
24576         } catch (e) {
24577             nodeRange.selectNodeContents(node);
24578         }
24579     
24580         var rangeStartRange = range.cloneRange();
24581         rangeStartRange.collapse(true);
24582     
24583         var rangeEndRange = range.cloneRange();
24584         rangeEndRange.collapse(false);
24585     
24586         var nodeStartRange = nodeRange.cloneRange();
24587         nodeStartRange.collapse(true);
24588     
24589         var nodeEndRange = nodeRange.cloneRange();
24590         nodeEndRange.collapse(false);
24591     
24592         return rangeStartRange.compareBoundaryPoints(
24593                  Range.START_TO_START, nodeEndRange) == -1 &&
24594                rangeEndRange.compareBoundaryPoints(
24595                  Range.START_TO_START, nodeStartRange) == 1;
24596         
24597          
24598     },
24599     rangeCompareNode : function(range, node)
24600     {
24601         var nodeRange = node.ownerDocument.createRange();
24602         try {
24603             nodeRange.selectNode(node);
24604         } catch (e) {
24605             nodeRange.selectNodeContents(node);
24606         }
24607         
24608         
24609         range.collapse(true);
24610     
24611         nodeRange.collapse(true);
24612      
24613         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24614         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24615          
24616         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24617         
24618         var nodeIsBefore   =  ss == 1;
24619         var nodeIsAfter    = ee == -1;
24620         
24621         if (nodeIsBefore && nodeIsAfter) {
24622             return 0; // outer
24623         }
24624         if (!nodeIsBefore && nodeIsAfter) {
24625             return 1; //right trailed.
24626         }
24627         
24628         if (nodeIsBefore && !nodeIsAfter) {
24629             return 2;  // left trailed.
24630         }
24631         // fully contined.
24632         return 3;
24633     },
24634
24635     // private? - in a new class?
24636     cleanUpPaste :  function()
24637     {
24638         // cleans up the whole document..
24639         Roo.log('cleanuppaste');
24640         
24641         this.cleanUpChildren(this.doc.body);
24642         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24643         if (clean != this.doc.body.innerHTML) {
24644             this.doc.body.innerHTML = clean;
24645         }
24646         
24647     },
24648     
24649     cleanWordChars : function(input) {// change the chars to hex code
24650         var he = Roo.HtmlEditorCore;
24651         
24652         var output = input;
24653         Roo.each(he.swapCodes, function(sw) { 
24654             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24655             
24656             output = output.replace(swapper, sw[1]);
24657         });
24658         
24659         return output;
24660     },
24661     
24662     
24663     cleanUpChildren : function (n)
24664     {
24665         if (!n.childNodes.length) {
24666             return;
24667         }
24668         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24669            this.cleanUpChild(n.childNodes[i]);
24670         }
24671     },
24672     
24673     
24674         
24675     
24676     cleanUpChild : function (node)
24677     {
24678         var ed = this;
24679         //console.log(node);
24680         if (node.nodeName == "#text") {
24681             // clean up silly Windows -- stuff?
24682             return; 
24683         }
24684         if (node.nodeName == "#comment") {
24685             node.parentNode.removeChild(node);
24686             // clean up silly Windows -- stuff?
24687             return; 
24688         }
24689         var lcname = node.tagName.toLowerCase();
24690         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24691         // whitelist of tags..
24692         
24693         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24694             // remove node.
24695             node.parentNode.removeChild(node);
24696             return;
24697             
24698         }
24699         
24700         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24701         
24702         // spans with no attributes - just remove them..
24703         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24704             remove_keep_children = true;
24705         }
24706         
24707         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24708         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24709         
24710         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24711         //    remove_keep_children = true;
24712         //}
24713         
24714         if (remove_keep_children) {
24715             this.cleanUpChildren(node);
24716             // inserts everything just before this node...
24717             while (node.childNodes.length) {
24718                 var cn = node.childNodes[0];
24719                 node.removeChild(cn);
24720                 node.parentNode.insertBefore(cn, node);
24721             }
24722             node.parentNode.removeChild(node);
24723             return;
24724         }
24725         
24726         if (!node.attributes || !node.attributes.length) {
24727             
24728           
24729             
24730             
24731             this.cleanUpChildren(node);
24732             return;
24733         }
24734         
24735         function cleanAttr(n,v)
24736         {
24737             
24738             if (v.match(/^\./) || v.match(/^\//)) {
24739                 return;
24740             }
24741             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24742                 return;
24743             }
24744             if (v.match(/^#/)) {
24745                 return;
24746             }
24747             if (v.match(/^\{/)) { // allow template editing.
24748                 return;
24749             }
24750 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24751             node.removeAttribute(n);
24752             
24753         }
24754         
24755         var cwhite = this.cwhite;
24756         var cblack = this.cblack;
24757             
24758         function cleanStyle(n,v)
24759         {
24760             if (v.match(/expression/)) { //XSS?? should we even bother..
24761                 node.removeAttribute(n);
24762                 return;
24763             }
24764             
24765             var parts = v.split(/;/);
24766             var clean = [];
24767             
24768             Roo.each(parts, function(p) {
24769                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24770                 if (!p.length) {
24771                     return true;
24772                 }
24773                 var l = p.split(':').shift().replace(/\s+/g,'');
24774                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24775                 
24776                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24777 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24778                     //node.removeAttribute(n);
24779                     return true;
24780                 }
24781                 //Roo.log()
24782                 // only allow 'c whitelisted system attributes'
24783                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24784 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24785                     //node.removeAttribute(n);
24786                     return true;
24787                 }
24788                 
24789                 
24790                  
24791                 
24792                 clean.push(p);
24793                 return true;
24794             });
24795             if (clean.length) { 
24796                 node.setAttribute(n, clean.join(';'));
24797             } else {
24798                 node.removeAttribute(n);
24799             }
24800             
24801         }
24802         
24803         
24804         for (var i = node.attributes.length-1; i > -1 ; i--) {
24805             var a = node.attributes[i];
24806             //console.log(a);
24807             
24808             if (a.name.toLowerCase().substr(0,2)=='on')  {
24809                 node.removeAttribute(a.name);
24810                 continue;
24811             }
24812             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24813                 node.removeAttribute(a.name);
24814                 continue;
24815             }
24816             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24817                 cleanAttr(a.name,a.value); // fixme..
24818                 continue;
24819             }
24820             if (a.name == 'style') {
24821                 cleanStyle(a.name,a.value);
24822                 continue;
24823             }
24824             /// clean up MS crap..
24825             // tecnically this should be a list of valid class'es..
24826             
24827             
24828             if (a.name == 'class') {
24829                 if (a.value.match(/^Mso/)) {
24830                     node.removeAttribute('class');
24831                 }
24832                 
24833                 if (a.value.match(/^body$/)) {
24834                     node.removeAttribute('class');
24835                 }
24836                 continue;
24837             }
24838             
24839             // style cleanup!?
24840             // class cleanup?
24841             
24842         }
24843         
24844         
24845         this.cleanUpChildren(node);
24846         
24847         
24848     },
24849     
24850     /**
24851      * Clean up MS wordisms...
24852      */
24853     cleanWord : function(node)
24854     {
24855         if (!node) {
24856             this.cleanWord(this.doc.body);
24857             return;
24858         }
24859         
24860         if(
24861                 node.nodeName == 'SPAN' &&
24862                 !node.hasAttributes() &&
24863                 node.childNodes.length == 1 &&
24864                 node.firstChild.nodeName == "#text"  
24865         ) {
24866             var textNode = node.firstChild;
24867             node.removeChild(textNode);
24868             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24869                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24870             }
24871             node.parentNode.insertBefore(textNode, node);
24872             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24873                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24874             }
24875             node.parentNode.removeChild(node);
24876         }
24877         
24878         if (node.nodeName == "#text") {
24879             // clean up silly Windows -- stuff?
24880             return; 
24881         }
24882         if (node.nodeName == "#comment") {
24883             node.parentNode.removeChild(node);
24884             // clean up silly Windows -- stuff?
24885             return; 
24886         }
24887         
24888         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24889             node.parentNode.removeChild(node);
24890             return;
24891         }
24892         //Roo.log(node.tagName);
24893         // remove - but keep children..
24894         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24895             //Roo.log('-- removed');
24896             while (node.childNodes.length) {
24897                 var cn = node.childNodes[0];
24898                 node.removeChild(cn);
24899                 node.parentNode.insertBefore(cn, node);
24900                 // move node to parent - and clean it..
24901                 this.cleanWord(cn);
24902             }
24903             node.parentNode.removeChild(node);
24904             /// no need to iterate chidlren = it's got none..
24905             //this.iterateChildren(node, this.cleanWord);
24906             return;
24907         }
24908         // clean styles
24909         if (node.className.length) {
24910             
24911             var cn = node.className.split(/\W+/);
24912             var cna = [];
24913             Roo.each(cn, function(cls) {
24914                 if (cls.match(/Mso[a-zA-Z]+/)) {
24915                     return;
24916                 }
24917                 cna.push(cls);
24918             });
24919             node.className = cna.length ? cna.join(' ') : '';
24920             if (!cna.length) {
24921                 node.removeAttribute("class");
24922             }
24923         }
24924         
24925         if (node.hasAttribute("lang")) {
24926             node.removeAttribute("lang");
24927         }
24928         
24929         if (node.hasAttribute("style")) {
24930             
24931             var styles = node.getAttribute("style").split(";");
24932             var nstyle = [];
24933             Roo.each(styles, function(s) {
24934                 if (!s.match(/:/)) {
24935                     return;
24936                 }
24937                 var kv = s.split(":");
24938                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24939                     return;
24940                 }
24941                 // what ever is left... we allow.
24942                 nstyle.push(s);
24943             });
24944             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24945             if (!nstyle.length) {
24946                 node.removeAttribute('style');
24947             }
24948         }
24949         this.iterateChildren(node, this.cleanWord);
24950         
24951         
24952         
24953     },
24954     /**
24955      * iterateChildren of a Node, calling fn each time, using this as the scole..
24956      * @param {DomNode} node node to iterate children of.
24957      * @param {Function} fn method of this class to call on each item.
24958      */
24959     iterateChildren : function(node, fn)
24960     {
24961         if (!node.childNodes.length) {
24962                 return;
24963         }
24964         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24965            fn.call(this, node.childNodes[i])
24966         }
24967     },
24968     
24969     
24970     /**
24971      * cleanTableWidths.
24972      *
24973      * Quite often pasting from word etc.. results in tables with column and widths.
24974      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24975      *
24976      */
24977     cleanTableWidths : function(node)
24978     {
24979          
24980          
24981         if (!node) {
24982             this.cleanTableWidths(this.doc.body);
24983             return;
24984         }
24985         
24986         // ignore list...
24987         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24988             return; 
24989         }
24990         Roo.log(node.tagName);
24991         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24992             this.iterateChildren(node, this.cleanTableWidths);
24993             return;
24994         }
24995         if (node.hasAttribute('width')) {
24996             node.removeAttribute('width');
24997         }
24998         
24999          
25000         if (node.hasAttribute("style")) {
25001             // pretty basic...
25002             
25003             var styles = node.getAttribute("style").split(";");
25004             var nstyle = [];
25005             Roo.each(styles, function(s) {
25006                 if (!s.match(/:/)) {
25007                     return;
25008                 }
25009                 var kv = s.split(":");
25010                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25011                     return;
25012                 }
25013                 // what ever is left... we allow.
25014                 nstyle.push(s);
25015             });
25016             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25017             if (!nstyle.length) {
25018                 node.removeAttribute('style');
25019             }
25020         }
25021         
25022         this.iterateChildren(node, this.cleanTableWidths);
25023         
25024         
25025     },
25026     
25027     
25028     
25029     
25030     domToHTML : function(currentElement, depth, nopadtext) {
25031         
25032         depth = depth || 0;
25033         nopadtext = nopadtext || false;
25034     
25035         if (!currentElement) {
25036             return this.domToHTML(this.doc.body);
25037         }
25038         
25039         //Roo.log(currentElement);
25040         var j;
25041         var allText = false;
25042         var nodeName = currentElement.nodeName;
25043         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25044         
25045         if  (nodeName == '#text') {
25046             
25047             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25048         }
25049         
25050         
25051         var ret = '';
25052         if (nodeName != 'BODY') {
25053              
25054             var i = 0;
25055             // Prints the node tagName, such as <A>, <IMG>, etc
25056             if (tagName) {
25057                 var attr = [];
25058                 for(i = 0; i < currentElement.attributes.length;i++) {
25059                     // quoting?
25060                     var aname = currentElement.attributes.item(i).name;
25061                     if (!currentElement.attributes.item(i).value.length) {
25062                         continue;
25063                     }
25064                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25065                 }
25066                 
25067                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25068             } 
25069             else {
25070                 
25071                 // eack
25072             }
25073         } else {
25074             tagName = false;
25075         }
25076         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25077             return ret;
25078         }
25079         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25080             nopadtext = true;
25081         }
25082         
25083         
25084         // Traverse the tree
25085         i = 0;
25086         var currentElementChild = currentElement.childNodes.item(i);
25087         var allText = true;
25088         var innerHTML  = '';
25089         lastnode = '';
25090         while (currentElementChild) {
25091             // Formatting code (indent the tree so it looks nice on the screen)
25092             var nopad = nopadtext;
25093             if (lastnode == 'SPAN') {
25094                 nopad  = true;
25095             }
25096             // text
25097             if  (currentElementChild.nodeName == '#text') {
25098                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25099                 toadd = nopadtext ? toadd : toadd.trim();
25100                 if (!nopad && toadd.length > 80) {
25101                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25102                 }
25103                 innerHTML  += toadd;
25104                 
25105                 i++;
25106                 currentElementChild = currentElement.childNodes.item(i);
25107                 lastNode = '';
25108                 continue;
25109             }
25110             allText = false;
25111             
25112             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25113                 
25114             // Recursively traverse the tree structure of the child node
25115             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25116             lastnode = currentElementChild.nodeName;
25117             i++;
25118             currentElementChild=currentElement.childNodes.item(i);
25119         }
25120         
25121         ret += innerHTML;
25122         
25123         if (!allText) {
25124                 // The remaining code is mostly for formatting the tree
25125             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25126         }
25127         
25128         
25129         if (tagName) {
25130             ret+= "</"+tagName+">";
25131         }
25132         return ret;
25133         
25134     },
25135         
25136     applyBlacklists : function()
25137     {
25138         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25139         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25140         
25141         this.white = [];
25142         this.black = [];
25143         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25144             if (b.indexOf(tag) > -1) {
25145                 return;
25146             }
25147             this.white.push(tag);
25148             
25149         }, this);
25150         
25151         Roo.each(w, function(tag) {
25152             if (b.indexOf(tag) > -1) {
25153                 return;
25154             }
25155             if (this.white.indexOf(tag) > -1) {
25156                 return;
25157             }
25158             this.white.push(tag);
25159             
25160         }, this);
25161         
25162         
25163         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25164             if (w.indexOf(tag) > -1) {
25165                 return;
25166             }
25167             this.black.push(tag);
25168             
25169         }, this);
25170         
25171         Roo.each(b, function(tag) {
25172             if (w.indexOf(tag) > -1) {
25173                 return;
25174             }
25175             if (this.black.indexOf(tag) > -1) {
25176                 return;
25177             }
25178             this.black.push(tag);
25179             
25180         }, this);
25181         
25182         
25183         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25184         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25185         
25186         this.cwhite = [];
25187         this.cblack = [];
25188         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25189             if (b.indexOf(tag) > -1) {
25190                 return;
25191             }
25192             this.cwhite.push(tag);
25193             
25194         }, this);
25195         
25196         Roo.each(w, function(tag) {
25197             if (b.indexOf(tag) > -1) {
25198                 return;
25199             }
25200             if (this.cwhite.indexOf(tag) > -1) {
25201                 return;
25202             }
25203             this.cwhite.push(tag);
25204             
25205         }, this);
25206         
25207         
25208         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25209             if (w.indexOf(tag) > -1) {
25210                 return;
25211             }
25212             this.cblack.push(tag);
25213             
25214         }, this);
25215         
25216         Roo.each(b, function(tag) {
25217             if (w.indexOf(tag) > -1) {
25218                 return;
25219             }
25220             if (this.cblack.indexOf(tag) > -1) {
25221                 return;
25222             }
25223             this.cblack.push(tag);
25224             
25225         }, this);
25226     },
25227     
25228     setStylesheets : function(stylesheets)
25229     {
25230         if(typeof(stylesheets) == 'string'){
25231             Roo.get(this.iframe.contentDocument.head).createChild({
25232                 tag : 'link',
25233                 rel : 'stylesheet',
25234                 type : 'text/css',
25235                 href : stylesheets
25236             });
25237             
25238             return;
25239         }
25240         var _this = this;
25241      
25242         Roo.each(stylesheets, function(s) {
25243             if(!s.length){
25244                 return;
25245             }
25246             
25247             Roo.get(_this.iframe.contentDocument.head).createChild({
25248                 tag : 'link',
25249                 rel : 'stylesheet',
25250                 type : 'text/css',
25251                 href : s
25252             });
25253         });
25254
25255         
25256     },
25257     
25258     removeStylesheets : function()
25259     {
25260         var _this = this;
25261         
25262         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25263             s.remove();
25264         });
25265     },
25266     
25267     setStyle : function(style)
25268     {
25269         Roo.get(this.iframe.contentDocument.head).createChild({
25270             tag : 'style',
25271             type : 'text/css',
25272             html : style
25273         });
25274
25275         return;
25276     }
25277     
25278     // hide stuff that is not compatible
25279     /**
25280      * @event blur
25281      * @hide
25282      */
25283     /**
25284      * @event change
25285      * @hide
25286      */
25287     /**
25288      * @event focus
25289      * @hide
25290      */
25291     /**
25292      * @event specialkey
25293      * @hide
25294      */
25295     /**
25296      * @cfg {String} fieldClass @hide
25297      */
25298     /**
25299      * @cfg {String} focusClass @hide
25300      */
25301     /**
25302      * @cfg {String} autoCreate @hide
25303      */
25304     /**
25305      * @cfg {String} inputType @hide
25306      */
25307     /**
25308      * @cfg {String} invalidClass @hide
25309      */
25310     /**
25311      * @cfg {String} invalidText @hide
25312      */
25313     /**
25314      * @cfg {String} msgFx @hide
25315      */
25316     /**
25317      * @cfg {String} validateOnBlur @hide
25318      */
25319 });
25320
25321 Roo.HtmlEditorCore.white = [
25322         'area', 'br', 'img', 'input', 'hr', 'wbr',
25323         
25324        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25325        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25326        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25327        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25328        'table',   'ul',         'xmp', 
25329        
25330        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25331       'thead',   'tr', 
25332      
25333       'dir', 'menu', 'ol', 'ul', 'dl',
25334        
25335       'embed',  'object'
25336 ];
25337
25338
25339 Roo.HtmlEditorCore.black = [
25340     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25341         'applet', // 
25342         'base',   'basefont', 'bgsound', 'blink',  'body', 
25343         'frame',  'frameset', 'head',    'html',   'ilayer', 
25344         'iframe', 'layer',  'link',     'meta',    'object',   
25345         'script', 'style' ,'title',  'xml' // clean later..
25346 ];
25347 Roo.HtmlEditorCore.clean = [
25348     'script', 'style', 'title', 'xml'
25349 ];
25350 Roo.HtmlEditorCore.remove = [
25351     'font'
25352 ];
25353 // attributes..
25354
25355 Roo.HtmlEditorCore.ablack = [
25356     'on'
25357 ];
25358     
25359 Roo.HtmlEditorCore.aclean = [ 
25360     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25361 ];
25362
25363 // protocols..
25364 Roo.HtmlEditorCore.pwhite= [
25365         'http',  'https',  'mailto'
25366 ];
25367
25368 // white listed style attributes.
25369 Roo.HtmlEditorCore.cwhite= [
25370       //  'text-align', /// default is to allow most things..
25371       
25372          
25373 //        'font-size'//??
25374 ];
25375
25376 // black listed style attributes.
25377 Roo.HtmlEditorCore.cblack= [
25378       //  'font-size' -- this can be set by the project 
25379 ];
25380
25381
25382 Roo.HtmlEditorCore.swapCodes   =[ 
25383     [    8211, "--" ], 
25384     [    8212, "--" ], 
25385     [    8216,  "'" ],  
25386     [    8217, "'" ],  
25387     [    8220, '"' ],  
25388     [    8221, '"' ],  
25389     [    8226, "*" ],  
25390     [    8230, "..." ]
25391 ]; 
25392
25393     /*
25394  * - LGPL
25395  *
25396  * HtmlEditor
25397  * 
25398  */
25399
25400 /**
25401  * @class Roo.bootstrap.HtmlEditor
25402  * @extends Roo.bootstrap.TextArea
25403  * Bootstrap HtmlEditor class
25404
25405  * @constructor
25406  * Create a new HtmlEditor
25407  * @param {Object} config The config object
25408  */
25409
25410 Roo.bootstrap.HtmlEditor = function(config){
25411     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25412     if (!this.toolbars) {
25413         this.toolbars = [];
25414     }
25415     
25416     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25417     this.addEvents({
25418             /**
25419              * @event initialize
25420              * Fires when the editor is fully initialized (including the iframe)
25421              * @param {HtmlEditor} this
25422              */
25423             initialize: true,
25424             /**
25425              * @event activate
25426              * Fires when the editor is first receives the focus. Any insertion must wait
25427              * until after this event.
25428              * @param {HtmlEditor} this
25429              */
25430             activate: true,
25431              /**
25432              * @event beforesync
25433              * Fires before the textarea is updated with content from the editor iframe. Return false
25434              * to cancel the sync.
25435              * @param {HtmlEditor} this
25436              * @param {String} html
25437              */
25438             beforesync: true,
25439              /**
25440              * @event beforepush
25441              * Fires before the iframe editor is updated with content from the textarea. Return false
25442              * to cancel the push.
25443              * @param {HtmlEditor} this
25444              * @param {String} html
25445              */
25446             beforepush: true,
25447              /**
25448              * @event sync
25449              * Fires when the textarea is updated with content from the editor iframe.
25450              * @param {HtmlEditor} this
25451              * @param {String} html
25452              */
25453             sync: true,
25454              /**
25455              * @event push
25456              * Fires when the iframe editor is updated with content from the textarea.
25457              * @param {HtmlEditor} this
25458              * @param {String} html
25459              */
25460             push: true,
25461              /**
25462              * @event editmodechange
25463              * Fires when the editor switches edit modes
25464              * @param {HtmlEditor} this
25465              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25466              */
25467             editmodechange: true,
25468             /**
25469              * @event editorevent
25470              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25471              * @param {HtmlEditor} this
25472              */
25473             editorevent: true,
25474             /**
25475              * @event firstfocus
25476              * Fires when on first focus - needed by toolbars..
25477              * @param {HtmlEditor} this
25478              */
25479             firstfocus: true,
25480             /**
25481              * @event autosave
25482              * Auto save the htmlEditor value as a file into Events
25483              * @param {HtmlEditor} this
25484              */
25485             autosave: true,
25486             /**
25487              * @event savedpreview
25488              * preview the saved version of htmlEditor
25489              * @param {HtmlEditor} this
25490              */
25491             savedpreview: true
25492         });
25493 };
25494
25495
25496 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25497     
25498     
25499       /**
25500      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25501      */
25502     toolbars : false,
25503     
25504      /**
25505     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25506     */
25507     btns : [],
25508    
25509      /**
25510      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25511      *                        Roo.resizable.
25512      */
25513     resizable : false,
25514      /**
25515      * @cfg {Number} height (in pixels)
25516      */   
25517     height: 300,
25518    /**
25519      * @cfg {Number} width (in pixels)
25520      */   
25521     width: false,
25522     
25523     /**
25524      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25525      * 
25526      */
25527     stylesheets: false,
25528     
25529     // id of frame..
25530     frameId: false,
25531     
25532     // private properties
25533     validationEvent : false,
25534     deferHeight: true,
25535     initialized : false,
25536     activated : false,
25537     
25538     onFocus : Roo.emptyFn,
25539     iframePad:3,
25540     hideMode:'offsets',
25541     
25542     tbContainer : false,
25543     
25544     bodyCls : '',
25545     
25546     toolbarContainer :function() {
25547         return this.wrap.select('.x-html-editor-tb',true).first();
25548     },
25549
25550     /**
25551      * Protected method that will not generally be called directly. It
25552      * is called when the editor creates its toolbar. Override this method if you need to
25553      * add custom toolbar buttons.
25554      * @param {HtmlEditor} editor
25555      */
25556     createToolbar : function(){
25557         Roo.log('renewing');
25558         Roo.log("create toolbars");
25559         
25560         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25561         this.toolbars[0].render(this.toolbarContainer());
25562         
25563         return;
25564         
25565 //        if (!editor.toolbars || !editor.toolbars.length) {
25566 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25567 //        }
25568 //        
25569 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25570 //            editor.toolbars[i] = Roo.factory(
25571 //                    typeof(editor.toolbars[i]) == 'string' ?
25572 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25573 //                Roo.bootstrap.HtmlEditor);
25574 //            editor.toolbars[i].init(editor);
25575 //        }
25576     },
25577
25578      
25579     // private
25580     onRender : function(ct, position)
25581     {
25582        // Roo.log("Call onRender: " + this.xtype);
25583         var _t = this;
25584         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25585       
25586         this.wrap = this.inputEl().wrap({
25587             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25588         });
25589         
25590         this.editorcore.onRender(ct, position);
25591          
25592         if (this.resizable) {
25593             this.resizeEl = new Roo.Resizable(this.wrap, {
25594                 pinned : true,
25595                 wrap: true,
25596                 dynamic : true,
25597                 minHeight : this.height,
25598                 height: this.height,
25599                 handles : this.resizable,
25600                 width: this.width,
25601                 listeners : {
25602                     resize : function(r, w, h) {
25603                         _t.onResize(w,h); // -something
25604                     }
25605                 }
25606             });
25607             
25608         }
25609         this.createToolbar(this);
25610        
25611         
25612         if(!this.width && this.resizable){
25613             this.setSize(this.wrap.getSize());
25614         }
25615         if (this.resizeEl) {
25616             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25617             // should trigger onReize..
25618         }
25619         
25620     },
25621
25622     // private
25623     onResize : function(w, h)
25624     {
25625         Roo.log('resize: ' +w + ',' + h );
25626         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25627         var ew = false;
25628         var eh = false;
25629         
25630         if(this.inputEl() ){
25631             if(typeof w == 'number'){
25632                 var aw = w - this.wrap.getFrameWidth('lr');
25633                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25634                 ew = aw;
25635             }
25636             if(typeof h == 'number'){
25637                  var tbh = -11;  // fixme it needs to tool bar size!
25638                 for (var i =0; i < this.toolbars.length;i++) {
25639                     // fixme - ask toolbars for heights?
25640                     tbh += this.toolbars[i].el.getHeight();
25641                     //if (this.toolbars[i].footer) {
25642                     //    tbh += this.toolbars[i].footer.el.getHeight();
25643                     //}
25644                 }
25645               
25646                 
25647                 
25648                 
25649                 
25650                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25651                 ah -= 5; // knock a few pixes off for look..
25652                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25653                 var eh = ah;
25654             }
25655         }
25656         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25657         this.editorcore.onResize(ew,eh);
25658         
25659     },
25660
25661     /**
25662      * Toggles the editor between standard and source edit mode.
25663      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25664      */
25665     toggleSourceEdit : function(sourceEditMode)
25666     {
25667         this.editorcore.toggleSourceEdit(sourceEditMode);
25668         
25669         if(this.editorcore.sourceEditMode){
25670             Roo.log('editor - showing textarea');
25671             
25672 //            Roo.log('in');
25673 //            Roo.log(this.syncValue());
25674             this.syncValue();
25675             this.inputEl().removeClass(['hide', 'x-hidden']);
25676             this.inputEl().dom.removeAttribute('tabIndex');
25677             this.inputEl().focus();
25678         }else{
25679             Roo.log('editor - hiding textarea');
25680 //            Roo.log('out')
25681 //            Roo.log(this.pushValue()); 
25682             this.pushValue();
25683             
25684             this.inputEl().addClass(['hide', 'x-hidden']);
25685             this.inputEl().dom.setAttribute('tabIndex', -1);
25686             //this.deferFocus();
25687         }
25688          
25689         if(this.resizable){
25690             this.setSize(this.wrap.getSize());
25691         }
25692         
25693         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25694     },
25695  
25696     // private (for BoxComponent)
25697     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25698
25699     // private (for BoxComponent)
25700     getResizeEl : function(){
25701         return this.wrap;
25702     },
25703
25704     // private (for BoxComponent)
25705     getPositionEl : function(){
25706         return this.wrap;
25707     },
25708
25709     // private
25710     initEvents : function(){
25711         this.originalValue = this.getValue();
25712     },
25713
25714 //    /**
25715 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25716 //     * @method
25717 //     */
25718 //    markInvalid : Roo.emptyFn,
25719 //    /**
25720 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25721 //     * @method
25722 //     */
25723 //    clearInvalid : Roo.emptyFn,
25724
25725     setValue : function(v){
25726         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25727         this.editorcore.pushValue();
25728     },
25729
25730      
25731     // private
25732     deferFocus : function(){
25733         this.focus.defer(10, this);
25734     },
25735
25736     // doc'ed in Field
25737     focus : function(){
25738         this.editorcore.focus();
25739         
25740     },
25741       
25742
25743     // private
25744     onDestroy : function(){
25745         
25746         
25747         
25748         if(this.rendered){
25749             
25750             for (var i =0; i < this.toolbars.length;i++) {
25751                 // fixme - ask toolbars for heights?
25752                 this.toolbars[i].onDestroy();
25753             }
25754             
25755             this.wrap.dom.innerHTML = '';
25756             this.wrap.remove();
25757         }
25758     },
25759
25760     // private
25761     onFirstFocus : function(){
25762         //Roo.log("onFirstFocus");
25763         this.editorcore.onFirstFocus();
25764          for (var i =0; i < this.toolbars.length;i++) {
25765             this.toolbars[i].onFirstFocus();
25766         }
25767         
25768     },
25769     
25770     // private
25771     syncValue : function()
25772     {   
25773         this.editorcore.syncValue();
25774     },
25775     
25776     pushValue : function()
25777     {   
25778         this.editorcore.pushValue();
25779     }
25780      
25781     
25782     // hide stuff that is not compatible
25783     /**
25784      * @event blur
25785      * @hide
25786      */
25787     /**
25788      * @event change
25789      * @hide
25790      */
25791     /**
25792      * @event focus
25793      * @hide
25794      */
25795     /**
25796      * @event specialkey
25797      * @hide
25798      */
25799     /**
25800      * @cfg {String} fieldClass @hide
25801      */
25802     /**
25803      * @cfg {String} focusClass @hide
25804      */
25805     /**
25806      * @cfg {String} autoCreate @hide
25807      */
25808     /**
25809      * @cfg {String} inputType @hide
25810      */
25811      
25812     /**
25813      * @cfg {String} invalidText @hide
25814      */
25815     /**
25816      * @cfg {String} msgFx @hide
25817      */
25818     /**
25819      * @cfg {String} validateOnBlur @hide
25820      */
25821 });
25822  
25823     
25824    
25825    
25826    
25827       
25828 Roo.namespace('Roo.bootstrap.htmleditor');
25829 /**
25830  * @class Roo.bootstrap.HtmlEditorToolbar1
25831  * Basic Toolbar
25832  * 
25833  * @example
25834  * Usage:
25835  *
25836  new Roo.bootstrap.HtmlEditor({
25837     ....
25838     toolbars : [
25839         new Roo.bootstrap.HtmlEditorToolbar1({
25840             disable : { fonts: 1 , format: 1, ..., ... , ...],
25841             btns : [ .... ]
25842         })
25843     }
25844      
25845  * 
25846  * @cfg {Object} disable List of elements to disable..
25847  * @cfg {Array} btns List of additional buttons.
25848  * 
25849  * 
25850  * NEEDS Extra CSS? 
25851  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25852  */
25853  
25854 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25855 {
25856     
25857     Roo.apply(this, config);
25858     
25859     // default disabled, based on 'good practice'..
25860     this.disable = this.disable || {};
25861     Roo.applyIf(this.disable, {
25862         fontSize : true,
25863         colors : true,
25864         specialElements : true
25865     });
25866     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25867     
25868     this.editor = config.editor;
25869     this.editorcore = config.editor.editorcore;
25870     
25871     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25872     
25873     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25874     // dont call parent... till later.
25875 }
25876 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25877      
25878     bar : true,
25879     
25880     editor : false,
25881     editorcore : false,
25882     
25883     
25884     formats : [
25885         "p" ,  
25886         "h1","h2","h3","h4","h5","h6", 
25887         "pre", "code", 
25888         "abbr", "acronym", "address", "cite", "samp", "var",
25889         'div','span'
25890     ],
25891     
25892     onRender : function(ct, position)
25893     {
25894        // Roo.log("Call onRender: " + this.xtype);
25895         
25896        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25897        Roo.log(this.el);
25898        this.el.dom.style.marginBottom = '0';
25899        var _this = this;
25900        var editorcore = this.editorcore;
25901        var editor= this.editor;
25902        
25903        var children = [];
25904        var btn = function(id,cmd , toggle, handler, html){
25905        
25906             var  event = toggle ? 'toggle' : 'click';
25907        
25908             var a = {
25909                 size : 'sm',
25910                 xtype: 'Button',
25911                 xns: Roo.bootstrap,
25912                 //glyphicon : id,
25913                 fa: id,
25914                 cmd : id || cmd,
25915                 enableToggle:toggle !== false,
25916                 html : html || '',
25917                 pressed : toggle ? false : null,
25918                 listeners : {}
25919             };
25920             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25921                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25922             };
25923             children.push(a);
25924             return a;
25925        }
25926        
25927     //    var cb_box = function...
25928         
25929         var style = {
25930                 xtype: 'Button',
25931                 size : 'sm',
25932                 xns: Roo.bootstrap,
25933                 fa : 'font',
25934                 //html : 'submit'
25935                 menu : {
25936                     xtype: 'Menu',
25937                     xns: Roo.bootstrap,
25938                     items:  []
25939                 }
25940         };
25941         Roo.each(this.formats, function(f) {
25942             style.menu.items.push({
25943                 xtype :'MenuItem',
25944                 xns: Roo.bootstrap,
25945                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25946                 tagname : f,
25947                 listeners : {
25948                     click : function()
25949                     {
25950                         editorcore.insertTag(this.tagname);
25951                         editor.focus();
25952                     }
25953                 }
25954                 
25955             });
25956         });
25957         children.push(style);   
25958         
25959         btn('bold',false,true);
25960         btn('italic',false,true);
25961         btn('align-left', 'justifyleft',true);
25962         btn('align-center', 'justifycenter',true);
25963         btn('align-right' , 'justifyright',true);
25964         btn('link', false, false, function(btn) {
25965             //Roo.log("create link?");
25966             var url = prompt(this.createLinkText, this.defaultLinkValue);
25967             if(url && url != 'http:/'+'/'){
25968                 this.editorcore.relayCmd('createlink', url);
25969             }
25970         }),
25971         btn('list','insertunorderedlist',true);
25972         btn('pencil', false,true, function(btn){
25973                 Roo.log(this);
25974                 this.toggleSourceEdit(btn.pressed);
25975         });
25976         
25977         if (this.editor.btns.length > 0) {
25978             for (var i = 0; i<this.editor.btns.length; i++) {
25979                 children.push(this.editor.btns[i]);
25980             }
25981         }
25982         
25983         /*
25984         var cog = {
25985                 xtype: 'Button',
25986                 size : 'sm',
25987                 xns: Roo.bootstrap,
25988                 glyphicon : 'cog',
25989                 //html : 'submit'
25990                 menu : {
25991                     xtype: 'Menu',
25992                     xns: Roo.bootstrap,
25993                     items:  []
25994                 }
25995         };
25996         
25997         cog.menu.items.push({
25998             xtype :'MenuItem',
25999             xns: Roo.bootstrap,
26000             html : Clean styles,
26001             tagname : f,
26002             listeners : {
26003                 click : function()
26004                 {
26005                     editorcore.insertTag(this.tagname);
26006                     editor.focus();
26007                 }
26008             }
26009             
26010         });
26011        */
26012         
26013          
26014        this.xtype = 'NavSimplebar';
26015         
26016         for(var i=0;i< children.length;i++) {
26017             
26018             this.buttons.add(this.addxtypeChild(children[i]));
26019             
26020         }
26021         
26022         editor.on('editorevent', this.updateToolbar, this);
26023     },
26024     onBtnClick : function(id)
26025     {
26026        this.editorcore.relayCmd(id);
26027        this.editorcore.focus();
26028     },
26029     
26030     /**
26031      * Protected method that will not generally be called directly. It triggers
26032      * a toolbar update by reading the markup state of the current selection in the editor.
26033      */
26034     updateToolbar: function(){
26035
26036         if(!this.editorcore.activated){
26037             this.editor.onFirstFocus(); // is this neeed?
26038             return;
26039         }
26040
26041         var btns = this.buttons; 
26042         var doc = this.editorcore.doc;
26043         btns.get('bold').setActive(doc.queryCommandState('bold'));
26044         btns.get('italic').setActive(doc.queryCommandState('italic'));
26045         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26046         
26047         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26048         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26049         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26050         
26051         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26052         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26053          /*
26054         
26055         var ans = this.editorcore.getAllAncestors();
26056         if (this.formatCombo) {
26057             
26058             
26059             var store = this.formatCombo.store;
26060             this.formatCombo.setValue("");
26061             for (var i =0; i < ans.length;i++) {
26062                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26063                     // select it..
26064                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26065                     break;
26066                 }
26067             }
26068         }
26069         
26070         
26071         
26072         // hides menus... - so this cant be on a menu...
26073         Roo.bootstrap.MenuMgr.hideAll();
26074         */
26075         Roo.bootstrap.MenuMgr.hideAll();
26076         //this.editorsyncValue();
26077     },
26078     onFirstFocus: function() {
26079         this.buttons.each(function(item){
26080            item.enable();
26081         });
26082     },
26083     toggleSourceEdit : function(sourceEditMode){
26084         
26085           
26086         if(sourceEditMode){
26087             Roo.log("disabling buttons");
26088            this.buttons.each( function(item){
26089                 if(item.cmd != 'pencil'){
26090                     item.disable();
26091                 }
26092             });
26093           
26094         }else{
26095             Roo.log("enabling buttons");
26096             if(this.editorcore.initialized){
26097                 this.buttons.each( function(item){
26098                     item.enable();
26099                 });
26100             }
26101             
26102         }
26103         Roo.log("calling toggole on editor");
26104         // tell the editor that it's been pressed..
26105         this.editor.toggleSourceEdit(sourceEditMode);
26106        
26107     }
26108 });
26109
26110
26111
26112
26113  
26114 /*
26115  * - LGPL
26116  */
26117
26118 /**
26119  * @class Roo.bootstrap.Markdown
26120  * @extends Roo.bootstrap.TextArea
26121  * Bootstrap Showdown editable area
26122  * @cfg {string} content
26123  * 
26124  * @constructor
26125  * Create a new Showdown
26126  */
26127
26128 Roo.bootstrap.Markdown = function(config){
26129     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26130    
26131 };
26132
26133 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26134     
26135     editing :false,
26136     
26137     initEvents : function()
26138     {
26139         
26140         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26141         this.markdownEl = this.el.createChild({
26142             cls : 'roo-markdown-area'
26143         });
26144         this.inputEl().addClass('d-none');
26145         var v = this.getValue();
26146         if (v === false) {
26147             v = '';
26148         }
26149         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26150         this.markdownEl.on('click', this.toggleTextEdit, this);
26151         this.on('blur', this.toggleTextEdit, this);
26152         this.on('specialkey', this.resizeTextArea, this);
26153     },
26154     
26155     toggleTextEdit : function()
26156     {
26157         var sh = this.markdownEl.getHeight();
26158         this.inputEl().addClass('d-none');
26159         this.markdownEl.addClass('d-none');
26160         if (!this.editing) {
26161             // show editor?
26162             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26163             this.inputEl().removeClass('d-none');
26164             this.inputEl().focus();
26165             this.editing = true;
26166             return;
26167         }
26168         // show showdown...
26169         this.updateMarkdown();
26170         this.markdownEl.removeClass('d-none');
26171         this.editing = false;
26172         return;
26173     },
26174     updateMarkdown : function()
26175     {
26176         if (this.getValue() == '') {
26177             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder);
26178             return;
26179         }
26180         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26181     },
26182     
26183     resizeTextArea: function () {
26184         
26185         var sh = 100;
26186         Roo.log([sh, this.getValue().split("\n").length * 30]);
26187         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26188     },
26189     setValue : function(val)
26190     {
26191         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26192         if (!this.editing) {
26193             this.updateMarkdown();
26194         }
26195         
26196     },
26197     focus : function()
26198     {
26199         if (!this.editing) {
26200             this.toggleTextEdit();
26201         }
26202         
26203     }
26204
26205
26206 });
26207 /**
26208  * @class Roo.bootstrap.Table.AbstractSelectionModel
26209  * @extends Roo.util.Observable
26210  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26211  * implemented by descendant classes.  This class should not be directly instantiated.
26212  * @constructor
26213  */
26214 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26215     this.locked = false;
26216     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26217 };
26218
26219
26220 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26221     /** @ignore Called by the grid automatically. Do not call directly. */
26222     init : function(grid){
26223         this.grid = grid;
26224         this.initEvents();
26225     },
26226
26227     /**
26228      * Locks the selections.
26229      */
26230     lock : function(){
26231         this.locked = true;
26232     },
26233
26234     /**
26235      * Unlocks the selections.
26236      */
26237     unlock : function(){
26238         this.locked = false;
26239     },
26240
26241     /**
26242      * Returns true if the selections are locked.
26243      * @return {Boolean}
26244      */
26245     isLocked : function(){
26246         return this.locked;
26247     },
26248     
26249     
26250     initEvents : function ()
26251     {
26252         
26253     }
26254 });
26255 /**
26256  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26257  * @class Roo.bootstrap.Table.RowSelectionModel
26258  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26259  * It supports multiple selections and keyboard selection/navigation. 
26260  * @constructor
26261  * @param {Object} config
26262  */
26263
26264 Roo.bootstrap.Table.RowSelectionModel = function(config){
26265     Roo.apply(this, config);
26266     this.selections = new Roo.util.MixedCollection(false, function(o){
26267         return o.id;
26268     });
26269
26270     this.last = false;
26271     this.lastActive = false;
26272
26273     this.addEvents({
26274         /**
26275              * @event selectionchange
26276              * Fires when the selection changes
26277              * @param {SelectionModel} this
26278              */
26279             "selectionchange" : true,
26280         /**
26281              * @event afterselectionchange
26282              * Fires after the selection changes (eg. by key press or clicking)
26283              * @param {SelectionModel} this
26284              */
26285             "afterselectionchange" : true,
26286         /**
26287              * @event beforerowselect
26288              * Fires when a row is selected being selected, return false to cancel.
26289              * @param {SelectionModel} this
26290              * @param {Number} rowIndex The selected index
26291              * @param {Boolean} keepExisting False if other selections will be cleared
26292              */
26293             "beforerowselect" : true,
26294         /**
26295              * @event rowselect
26296              * Fires when a row is selected.
26297              * @param {SelectionModel} this
26298              * @param {Number} rowIndex The selected index
26299              * @param {Roo.data.Record} r The record
26300              */
26301             "rowselect" : true,
26302         /**
26303              * @event rowdeselect
26304              * Fires when a row is deselected.
26305              * @param {SelectionModel} this
26306              * @param {Number} rowIndex The selected index
26307              */
26308         "rowdeselect" : true
26309     });
26310     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26311     this.locked = false;
26312  };
26313
26314 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26315     /**
26316      * @cfg {Boolean} singleSelect
26317      * True to allow selection of only one row at a time (defaults to false)
26318      */
26319     singleSelect : false,
26320
26321     // private
26322     initEvents : function()
26323     {
26324
26325         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26326         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26327         //}else{ // allow click to work like normal
26328          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26329         //}
26330         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26331         this.grid.on("rowclick", this.handleMouseDown, this);
26332         
26333         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26334             "up" : function(e){
26335                 if(!e.shiftKey){
26336                     this.selectPrevious(e.shiftKey);
26337                 }else if(this.last !== false && this.lastActive !== false){
26338                     var last = this.last;
26339                     this.selectRange(this.last,  this.lastActive-1);
26340                     this.grid.getView().focusRow(this.lastActive);
26341                     if(last !== false){
26342                         this.last = last;
26343                     }
26344                 }else{
26345                     this.selectFirstRow();
26346                 }
26347                 this.fireEvent("afterselectionchange", this);
26348             },
26349             "down" : function(e){
26350                 if(!e.shiftKey){
26351                     this.selectNext(e.shiftKey);
26352                 }else if(this.last !== false && this.lastActive !== false){
26353                     var last = this.last;
26354                     this.selectRange(this.last,  this.lastActive+1);
26355                     this.grid.getView().focusRow(this.lastActive);
26356                     if(last !== false){
26357                         this.last = last;
26358                     }
26359                 }else{
26360                     this.selectFirstRow();
26361                 }
26362                 this.fireEvent("afterselectionchange", this);
26363             },
26364             scope: this
26365         });
26366         this.grid.store.on('load', function(){
26367             this.selections.clear();
26368         },this);
26369         /*
26370         var view = this.grid.view;
26371         view.on("refresh", this.onRefresh, this);
26372         view.on("rowupdated", this.onRowUpdated, this);
26373         view.on("rowremoved", this.onRemove, this);
26374         */
26375     },
26376
26377     // private
26378     onRefresh : function()
26379     {
26380         var ds = this.grid.store, i, v = this.grid.view;
26381         var s = this.selections;
26382         s.each(function(r){
26383             if((i = ds.indexOfId(r.id)) != -1){
26384                 v.onRowSelect(i);
26385             }else{
26386                 s.remove(r);
26387             }
26388         });
26389     },
26390
26391     // private
26392     onRemove : function(v, index, r){
26393         this.selections.remove(r);
26394     },
26395
26396     // private
26397     onRowUpdated : function(v, index, r){
26398         if(this.isSelected(r)){
26399             v.onRowSelect(index);
26400         }
26401     },
26402
26403     /**
26404      * Select records.
26405      * @param {Array} records The records to select
26406      * @param {Boolean} keepExisting (optional) True to keep existing selections
26407      */
26408     selectRecords : function(records, keepExisting)
26409     {
26410         if(!keepExisting){
26411             this.clearSelections();
26412         }
26413             var ds = this.grid.store;
26414         for(var i = 0, len = records.length; i < len; i++){
26415             this.selectRow(ds.indexOf(records[i]), true);
26416         }
26417     },
26418
26419     /**
26420      * Gets the number of selected rows.
26421      * @return {Number}
26422      */
26423     getCount : function(){
26424         return this.selections.length;
26425     },
26426
26427     /**
26428      * Selects the first row in the grid.
26429      */
26430     selectFirstRow : function(){
26431         this.selectRow(0);
26432     },
26433
26434     /**
26435      * Select the last row.
26436      * @param {Boolean} keepExisting (optional) True to keep existing selections
26437      */
26438     selectLastRow : function(keepExisting){
26439         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26440         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26441     },
26442
26443     /**
26444      * Selects the row immediately following the last selected row.
26445      * @param {Boolean} keepExisting (optional) True to keep existing selections
26446      */
26447     selectNext : function(keepExisting)
26448     {
26449             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26450             this.selectRow(this.last+1, keepExisting);
26451             this.grid.getView().focusRow(this.last);
26452         }
26453     },
26454
26455     /**
26456      * Selects the row that precedes the last selected row.
26457      * @param {Boolean} keepExisting (optional) True to keep existing selections
26458      */
26459     selectPrevious : function(keepExisting){
26460         if(this.last){
26461             this.selectRow(this.last-1, keepExisting);
26462             this.grid.getView().focusRow(this.last);
26463         }
26464     },
26465
26466     /**
26467      * Returns the selected records
26468      * @return {Array} Array of selected records
26469      */
26470     getSelections : function(){
26471         return [].concat(this.selections.items);
26472     },
26473
26474     /**
26475      * Returns the first selected record.
26476      * @return {Record}
26477      */
26478     getSelected : function(){
26479         return this.selections.itemAt(0);
26480     },
26481
26482
26483     /**
26484      * Clears all selections.
26485      */
26486     clearSelections : function(fast)
26487     {
26488         if(this.locked) {
26489             return;
26490         }
26491         if(fast !== true){
26492                 var ds = this.grid.store;
26493             var s = this.selections;
26494             s.each(function(r){
26495                 this.deselectRow(ds.indexOfId(r.id));
26496             }, this);
26497             s.clear();
26498         }else{
26499             this.selections.clear();
26500         }
26501         this.last = false;
26502     },
26503
26504
26505     /**
26506      * Selects all rows.
26507      */
26508     selectAll : function(){
26509         if(this.locked) {
26510             return;
26511         }
26512         this.selections.clear();
26513         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26514             this.selectRow(i, true);
26515         }
26516     },
26517
26518     /**
26519      * Returns True if there is a selection.
26520      * @return {Boolean}
26521      */
26522     hasSelection : function(){
26523         return this.selections.length > 0;
26524     },
26525
26526     /**
26527      * Returns True if the specified row is selected.
26528      * @param {Number/Record} record The record or index of the record to check
26529      * @return {Boolean}
26530      */
26531     isSelected : function(index){
26532             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26533         return (r && this.selections.key(r.id) ? true : false);
26534     },
26535
26536     /**
26537      * Returns True if the specified record id is selected.
26538      * @param {String} id The id of record to check
26539      * @return {Boolean}
26540      */
26541     isIdSelected : function(id){
26542         return (this.selections.key(id) ? true : false);
26543     },
26544
26545
26546     // private
26547     handleMouseDBClick : function(e, t){
26548         
26549     },
26550     // private
26551     handleMouseDown : function(e, t)
26552     {
26553             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26554         if(this.isLocked() || rowIndex < 0 ){
26555             return;
26556         };
26557         if(e.shiftKey && this.last !== false){
26558             var last = this.last;
26559             this.selectRange(last, rowIndex, e.ctrlKey);
26560             this.last = last; // reset the last
26561             t.focus();
26562     
26563         }else{
26564             var isSelected = this.isSelected(rowIndex);
26565             //Roo.log("select row:" + rowIndex);
26566             if(isSelected){
26567                 this.deselectRow(rowIndex);
26568             } else {
26569                         this.selectRow(rowIndex, true);
26570             }
26571     
26572             /*
26573                 if(e.button !== 0 && isSelected){
26574                 alert('rowIndex 2: ' + rowIndex);
26575                     view.focusRow(rowIndex);
26576                 }else if(e.ctrlKey && isSelected){
26577                     this.deselectRow(rowIndex);
26578                 }else if(!isSelected){
26579                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26580                     view.focusRow(rowIndex);
26581                 }
26582             */
26583         }
26584         this.fireEvent("afterselectionchange", this);
26585     },
26586     // private
26587     handleDragableRowClick :  function(grid, rowIndex, e) 
26588     {
26589         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26590             this.selectRow(rowIndex, false);
26591             grid.view.focusRow(rowIndex);
26592              this.fireEvent("afterselectionchange", this);
26593         }
26594     },
26595     
26596     /**
26597      * Selects multiple rows.
26598      * @param {Array} rows Array of the indexes of the row to select
26599      * @param {Boolean} keepExisting (optional) True to keep existing selections
26600      */
26601     selectRows : function(rows, keepExisting){
26602         if(!keepExisting){
26603             this.clearSelections();
26604         }
26605         for(var i = 0, len = rows.length; i < len; i++){
26606             this.selectRow(rows[i], true);
26607         }
26608     },
26609
26610     /**
26611      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26612      * @param {Number} startRow The index of the first row in the range
26613      * @param {Number} endRow The index of the last row in the range
26614      * @param {Boolean} keepExisting (optional) True to retain existing selections
26615      */
26616     selectRange : function(startRow, endRow, keepExisting){
26617         if(this.locked) {
26618             return;
26619         }
26620         if(!keepExisting){
26621             this.clearSelections();
26622         }
26623         if(startRow <= endRow){
26624             for(var i = startRow; i <= endRow; i++){
26625                 this.selectRow(i, true);
26626             }
26627         }else{
26628             for(var i = startRow; i >= endRow; i--){
26629                 this.selectRow(i, true);
26630             }
26631         }
26632     },
26633
26634     /**
26635      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26636      * @param {Number} startRow The index of the first row in the range
26637      * @param {Number} endRow The index of the last row in the range
26638      */
26639     deselectRange : function(startRow, endRow, preventViewNotify){
26640         if(this.locked) {
26641             return;
26642         }
26643         for(var i = startRow; i <= endRow; i++){
26644             this.deselectRow(i, preventViewNotify);
26645         }
26646     },
26647
26648     /**
26649      * Selects a row.
26650      * @param {Number} row The index of the row to select
26651      * @param {Boolean} keepExisting (optional) True to keep existing selections
26652      */
26653     selectRow : function(index, keepExisting, preventViewNotify)
26654     {
26655             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26656             return;
26657         }
26658         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26659             if(!keepExisting || this.singleSelect){
26660                 this.clearSelections();
26661             }
26662             
26663             var r = this.grid.store.getAt(index);
26664             //console.log('selectRow - record id :' + r.id);
26665             
26666             this.selections.add(r);
26667             this.last = this.lastActive = index;
26668             if(!preventViewNotify){
26669                 var proxy = new Roo.Element(
26670                                 this.grid.getRowDom(index)
26671                 );
26672                 proxy.addClass('bg-info info');
26673             }
26674             this.fireEvent("rowselect", this, index, r);
26675             this.fireEvent("selectionchange", this);
26676         }
26677     },
26678
26679     /**
26680      * Deselects a row.
26681      * @param {Number} row The index of the row to deselect
26682      */
26683     deselectRow : function(index, preventViewNotify)
26684     {
26685         if(this.locked) {
26686             return;
26687         }
26688         if(this.last == index){
26689             this.last = false;
26690         }
26691         if(this.lastActive == index){
26692             this.lastActive = false;
26693         }
26694         
26695         var r = this.grid.store.getAt(index);
26696         if (!r) {
26697             return;
26698         }
26699         
26700         this.selections.remove(r);
26701         //.console.log('deselectRow - record id :' + r.id);
26702         if(!preventViewNotify){
26703         
26704             var proxy = new Roo.Element(
26705                 this.grid.getRowDom(index)
26706             );
26707             proxy.removeClass('bg-info info');
26708         }
26709         this.fireEvent("rowdeselect", this, index);
26710         this.fireEvent("selectionchange", this);
26711     },
26712
26713     // private
26714     restoreLast : function(){
26715         if(this._last){
26716             this.last = this._last;
26717         }
26718     },
26719
26720     // private
26721     acceptsNav : function(row, col, cm){
26722         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26723     },
26724
26725     // private
26726     onEditorKey : function(field, e){
26727         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26728         if(k == e.TAB){
26729             e.stopEvent();
26730             ed.completeEdit();
26731             if(e.shiftKey){
26732                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26733             }else{
26734                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26735             }
26736         }else if(k == e.ENTER && !e.ctrlKey){
26737             e.stopEvent();
26738             ed.completeEdit();
26739             if(e.shiftKey){
26740                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26741             }else{
26742                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26743             }
26744         }else if(k == e.ESC){
26745             ed.cancelEdit();
26746         }
26747         if(newCell){
26748             g.startEditing(newCell[0], newCell[1]);
26749         }
26750     }
26751 });
26752 /*
26753  * Based on:
26754  * Ext JS Library 1.1.1
26755  * Copyright(c) 2006-2007, Ext JS, LLC.
26756  *
26757  * Originally Released Under LGPL - original licence link has changed is not relivant.
26758  *
26759  * Fork - LGPL
26760  * <script type="text/javascript">
26761  */
26762  
26763 /**
26764  * @class Roo.bootstrap.PagingToolbar
26765  * @extends Roo.bootstrap.NavSimplebar
26766  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26767  * @constructor
26768  * Create a new PagingToolbar
26769  * @param {Object} config The config object
26770  * @param {Roo.data.Store} store
26771  */
26772 Roo.bootstrap.PagingToolbar = function(config)
26773 {
26774     // old args format still supported... - xtype is prefered..
26775         // created from xtype...
26776     
26777     this.ds = config.dataSource;
26778     
26779     if (config.store && !this.ds) {
26780         this.store= Roo.factory(config.store, Roo.data);
26781         this.ds = this.store;
26782         this.ds.xmodule = this.xmodule || false;
26783     }
26784     
26785     this.toolbarItems = [];
26786     if (config.items) {
26787         this.toolbarItems = config.items;
26788     }
26789     
26790     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26791     
26792     this.cursor = 0;
26793     
26794     if (this.ds) { 
26795         this.bind(this.ds);
26796     }
26797     
26798     if (Roo.bootstrap.version == 4) {
26799         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26800     } else {
26801         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26802     }
26803     
26804 };
26805
26806 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26807     /**
26808      * @cfg {Roo.data.Store} dataSource
26809      * The underlying data store providing the paged data
26810      */
26811     /**
26812      * @cfg {String/HTMLElement/Element} container
26813      * container The id or element that will contain the toolbar
26814      */
26815     /**
26816      * @cfg {Boolean} displayInfo
26817      * True to display the displayMsg (defaults to false)
26818      */
26819     /**
26820      * @cfg {Number} pageSize
26821      * The number of records to display per page (defaults to 20)
26822      */
26823     pageSize: 20,
26824     /**
26825      * @cfg {String} displayMsg
26826      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26827      */
26828     displayMsg : 'Displaying {0} - {1} of {2}',
26829     /**
26830      * @cfg {String} emptyMsg
26831      * The message to display when no records are found (defaults to "No data to display")
26832      */
26833     emptyMsg : 'No data to display',
26834     /**
26835      * Customizable piece of the default paging text (defaults to "Page")
26836      * @type String
26837      */
26838     beforePageText : "Page",
26839     /**
26840      * Customizable piece of the default paging text (defaults to "of %0")
26841      * @type String
26842      */
26843     afterPageText : "of {0}",
26844     /**
26845      * Customizable piece of the default paging text (defaults to "First Page")
26846      * @type String
26847      */
26848     firstText : "First Page",
26849     /**
26850      * Customizable piece of the default paging text (defaults to "Previous Page")
26851      * @type String
26852      */
26853     prevText : "Previous Page",
26854     /**
26855      * Customizable piece of the default paging text (defaults to "Next Page")
26856      * @type String
26857      */
26858     nextText : "Next Page",
26859     /**
26860      * Customizable piece of the default paging text (defaults to "Last Page")
26861      * @type String
26862      */
26863     lastText : "Last Page",
26864     /**
26865      * Customizable piece of the default paging text (defaults to "Refresh")
26866      * @type String
26867      */
26868     refreshText : "Refresh",
26869
26870     buttons : false,
26871     // private
26872     onRender : function(ct, position) 
26873     {
26874         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26875         this.navgroup.parentId = this.id;
26876         this.navgroup.onRender(this.el, null);
26877         // add the buttons to the navgroup
26878         
26879         if(this.displayInfo){
26880             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26881             this.displayEl = this.el.select('.x-paging-info', true).first();
26882 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26883 //            this.displayEl = navel.el.select('span',true).first();
26884         }
26885         
26886         var _this = this;
26887         
26888         if(this.buttons){
26889             Roo.each(_this.buttons, function(e){ // this might need to use render????
26890                Roo.factory(e).render(_this.el);
26891             });
26892         }
26893             
26894         Roo.each(_this.toolbarItems, function(e) {
26895             _this.navgroup.addItem(e);
26896         });
26897         
26898         
26899         this.first = this.navgroup.addItem({
26900             tooltip: this.firstText,
26901             cls: "prev btn-outline-secondary",
26902             html : ' <i class="fa fa-step-backward"></i>',
26903             disabled: true,
26904             preventDefault: true,
26905             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26906         });
26907         
26908         this.prev =  this.navgroup.addItem({
26909             tooltip: this.prevText,
26910             cls: "prev btn-outline-secondary",
26911             html : ' <i class="fa fa-backward"></i>',
26912             disabled: true,
26913             preventDefault: true,
26914             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26915         });
26916     //this.addSeparator();
26917         
26918         
26919         var field = this.navgroup.addItem( {
26920             tagtype : 'span',
26921             cls : 'x-paging-position  btn-outline-secondary',
26922              disabled: true,
26923             html : this.beforePageText  +
26924                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26925                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26926          } ); //?? escaped?
26927         
26928         this.field = field.el.select('input', true).first();
26929         this.field.on("keydown", this.onPagingKeydown, this);
26930         this.field.on("focus", function(){this.dom.select();});
26931     
26932     
26933         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26934         //this.field.setHeight(18);
26935         //this.addSeparator();
26936         this.next = this.navgroup.addItem({
26937             tooltip: this.nextText,
26938             cls: "next btn-outline-secondary",
26939             html : ' <i class="fa fa-forward"></i>',
26940             disabled: true,
26941             preventDefault: true,
26942             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26943         });
26944         this.last = this.navgroup.addItem({
26945             tooltip: this.lastText,
26946             html : ' <i class="fa fa-step-forward"></i>',
26947             cls: "next btn-outline-secondary",
26948             disabled: true,
26949             preventDefault: true,
26950             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26951         });
26952     //this.addSeparator();
26953         this.loading = this.navgroup.addItem({
26954             tooltip: this.refreshText,
26955             cls: "btn-outline-secondary",
26956             html : ' <i class="fa fa-refresh"></i>',
26957             preventDefault: true,
26958             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26959         });
26960         
26961     },
26962
26963     // private
26964     updateInfo : function(){
26965         if(this.displayEl){
26966             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26967             var msg = count == 0 ?
26968                 this.emptyMsg :
26969                 String.format(
26970                     this.displayMsg,
26971                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26972                 );
26973             this.displayEl.update(msg);
26974         }
26975     },
26976
26977     // private
26978     onLoad : function(ds, r, o)
26979     {
26980         this.cursor = o.params.start ? o.params.start : 0;
26981         
26982         var d = this.getPageData(),
26983             ap = d.activePage,
26984             ps = d.pages;
26985         
26986         
26987         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26988         this.field.dom.value = ap;
26989         this.first.setDisabled(ap == 1);
26990         this.prev.setDisabled(ap == 1);
26991         this.next.setDisabled(ap == ps);
26992         this.last.setDisabled(ap == ps);
26993         this.loading.enable();
26994         this.updateInfo();
26995     },
26996
26997     // private
26998     getPageData : function(){
26999         var total = this.ds.getTotalCount();
27000         return {
27001             total : total,
27002             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27003             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27004         };
27005     },
27006
27007     // private
27008     onLoadError : function(){
27009         this.loading.enable();
27010     },
27011
27012     // private
27013     onPagingKeydown : function(e){
27014         var k = e.getKey();
27015         var d = this.getPageData();
27016         if(k == e.RETURN){
27017             var v = this.field.dom.value, pageNum;
27018             if(!v || isNaN(pageNum = parseInt(v, 10))){
27019                 this.field.dom.value = d.activePage;
27020                 return;
27021             }
27022             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27023             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27024             e.stopEvent();
27025         }
27026         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))
27027         {
27028           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27029           this.field.dom.value = pageNum;
27030           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27031           e.stopEvent();
27032         }
27033         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27034         {
27035           var v = this.field.dom.value, pageNum; 
27036           var increment = (e.shiftKey) ? 10 : 1;
27037           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27038                 increment *= -1;
27039           }
27040           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27041             this.field.dom.value = d.activePage;
27042             return;
27043           }
27044           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27045           {
27046             this.field.dom.value = parseInt(v, 10) + increment;
27047             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27048             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27049           }
27050           e.stopEvent();
27051         }
27052     },
27053
27054     // private
27055     beforeLoad : function(){
27056         if(this.loading){
27057             this.loading.disable();
27058         }
27059     },
27060
27061     // private
27062     onClick : function(which){
27063         
27064         var ds = this.ds;
27065         if (!ds) {
27066             return;
27067         }
27068         
27069         switch(which){
27070             case "first":
27071                 ds.load({params:{start: 0, limit: this.pageSize}});
27072             break;
27073             case "prev":
27074                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27075             break;
27076             case "next":
27077                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27078             break;
27079             case "last":
27080                 var total = ds.getTotalCount();
27081                 var extra = total % this.pageSize;
27082                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27083                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27084             break;
27085             case "refresh":
27086                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27087             break;
27088         }
27089     },
27090
27091     /**
27092      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27093      * @param {Roo.data.Store} store The data store to unbind
27094      */
27095     unbind : function(ds){
27096         ds.un("beforeload", this.beforeLoad, this);
27097         ds.un("load", this.onLoad, this);
27098         ds.un("loadexception", this.onLoadError, this);
27099         ds.un("remove", this.updateInfo, this);
27100         ds.un("add", this.updateInfo, this);
27101         this.ds = undefined;
27102     },
27103
27104     /**
27105      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27106      * @param {Roo.data.Store} store The data store to bind
27107      */
27108     bind : function(ds){
27109         ds.on("beforeload", this.beforeLoad, this);
27110         ds.on("load", this.onLoad, this);
27111         ds.on("loadexception", this.onLoadError, this);
27112         ds.on("remove", this.updateInfo, this);
27113         ds.on("add", this.updateInfo, this);
27114         this.ds = ds;
27115     }
27116 });/*
27117  * - LGPL
27118  *
27119  * element
27120  * 
27121  */
27122
27123 /**
27124  * @class Roo.bootstrap.MessageBar
27125  * @extends Roo.bootstrap.Component
27126  * Bootstrap MessageBar class
27127  * @cfg {String} html contents of the MessageBar
27128  * @cfg {String} weight (info | success | warning | danger) default info
27129  * @cfg {String} beforeClass insert the bar before the given class
27130  * @cfg {Boolean} closable (true | false) default false
27131  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27132  * 
27133  * @constructor
27134  * Create a new Element
27135  * @param {Object} config The config object
27136  */
27137
27138 Roo.bootstrap.MessageBar = function(config){
27139     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27140 };
27141
27142 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27143     
27144     html: '',
27145     weight: 'info',
27146     closable: false,
27147     fixed: false,
27148     beforeClass: 'bootstrap-sticky-wrap',
27149     
27150     getAutoCreate : function(){
27151         
27152         var cfg = {
27153             tag: 'div',
27154             cls: 'alert alert-dismissable alert-' + this.weight,
27155             cn: [
27156                 {
27157                     tag: 'span',
27158                     cls: 'message',
27159                     html: this.html || ''
27160                 }
27161             ]
27162         };
27163         
27164         if(this.fixed){
27165             cfg.cls += ' alert-messages-fixed';
27166         }
27167         
27168         if(this.closable){
27169             cfg.cn.push({
27170                 tag: 'button',
27171                 cls: 'close',
27172                 html: 'x'
27173             });
27174         }
27175         
27176         return cfg;
27177     },
27178     
27179     onRender : function(ct, position)
27180     {
27181         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27182         
27183         if(!this.el){
27184             var cfg = Roo.apply({},  this.getAutoCreate());
27185             cfg.id = Roo.id();
27186             
27187             if (this.cls) {
27188                 cfg.cls += ' ' + this.cls;
27189             }
27190             if (this.style) {
27191                 cfg.style = this.style;
27192             }
27193             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27194             
27195             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27196         }
27197         
27198         this.el.select('>button.close').on('click', this.hide, this);
27199         
27200     },
27201     
27202     show : function()
27203     {
27204         if (!this.rendered) {
27205             this.render();
27206         }
27207         
27208         this.el.show();
27209         
27210         this.fireEvent('show', this);
27211         
27212     },
27213     
27214     hide : function()
27215     {
27216         if (!this.rendered) {
27217             this.render();
27218         }
27219         
27220         this.el.hide();
27221         
27222         this.fireEvent('hide', this);
27223     },
27224     
27225     update : function()
27226     {
27227 //        var e = this.el.dom.firstChild;
27228 //        
27229 //        if(this.closable){
27230 //            e = e.nextSibling;
27231 //        }
27232 //        
27233 //        e.data = this.html || '';
27234
27235         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27236     }
27237    
27238 });
27239
27240  
27241
27242      /*
27243  * - LGPL
27244  *
27245  * Graph
27246  * 
27247  */
27248
27249
27250 /**
27251  * @class Roo.bootstrap.Graph
27252  * @extends Roo.bootstrap.Component
27253  * Bootstrap Graph class
27254 > Prameters
27255  -sm {number} sm 4
27256  -md {number} md 5
27257  @cfg {String} graphtype  bar | vbar | pie
27258  @cfg {number} g_x coodinator | centre x (pie)
27259  @cfg {number} g_y coodinator | centre y (pie)
27260  @cfg {number} g_r radius (pie)
27261  @cfg {number} g_height height of the chart (respected by all elements in the set)
27262  @cfg {number} g_width width of the chart (respected by all elements in the set)
27263  @cfg {Object} title The title of the chart
27264     
27265  -{Array}  values
27266  -opts (object) options for the chart 
27267      o {
27268      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27269      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27270      o vgutter (number)
27271      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.
27272      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27273      o to
27274      o stretch (boolean)
27275      o }
27276  -opts (object) options for the pie
27277      o{
27278      o cut
27279      o startAngle (number)
27280      o endAngle (number)
27281      } 
27282  *
27283  * @constructor
27284  * Create a new Input
27285  * @param {Object} config The config object
27286  */
27287
27288 Roo.bootstrap.Graph = function(config){
27289     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27290     
27291     this.addEvents({
27292         // img events
27293         /**
27294          * @event click
27295          * The img click event for the img.
27296          * @param {Roo.EventObject} e
27297          */
27298         "click" : true
27299     });
27300 };
27301
27302 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27303     
27304     sm: 4,
27305     md: 5,
27306     graphtype: 'bar',
27307     g_height: 250,
27308     g_width: 400,
27309     g_x: 50,
27310     g_y: 50,
27311     g_r: 30,
27312     opts:{
27313         //g_colors: this.colors,
27314         g_type: 'soft',
27315         g_gutter: '20%'
27316
27317     },
27318     title : false,
27319
27320     getAutoCreate : function(){
27321         
27322         var cfg = {
27323             tag: 'div',
27324             html : null
27325         };
27326         
27327         
27328         return  cfg;
27329     },
27330
27331     onRender : function(ct,position){
27332         
27333         
27334         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27335         
27336         if (typeof(Raphael) == 'undefined') {
27337             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27338             return;
27339         }
27340         
27341         this.raphael = Raphael(this.el.dom);
27342         
27343                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27344                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27345                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27346                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27347                 /*
27348                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27349                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27350                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27351                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27352                 
27353                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27354                 r.barchart(330, 10, 300, 220, data1);
27355                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27356                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27357                 */
27358                 
27359                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27360                 // r.barchart(30, 30, 560, 250,  xdata, {
27361                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27362                 //     axis : "0 0 1 1",
27363                 //     axisxlabels :  xdata
27364                 //     //yvalues : cols,
27365                    
27366                 // });
27367 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27368 //        
27369 //        this.load(null,xdata,{
27370 //                axis : "0 0 1 1",
27371 //                axisxlabels :  xdata
27372 //                });
27373
27374     },
27375
27376     load : function(graphtype,xdata,opts)
27377     {
27378         this.raphael.clear();
27379         if(!graphtype) {
27380             graphtype = this.graphtype;
27381         }
27382         if(!opts){
27383             opts = this.opts;
27384         }
27385         var r = this.raphael,
27386             fin = function () {
27387                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27388             },
27389             fout = function () {
27390                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27391             },
27392             pfin = function() {
27393                 this.sector.stop();
27394                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27395
27396                 if (this.label) {
27397                     this.label[0].stop();
27398                     this.label[0].attr({ r: 7.5 });
27399                     this.label[1].attr({ "font-weight": 800 });
27400                 }
27401             },
27402             pfout = function() {
27403                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27404
27405                 if (this.label) {
27406                     this.label[0].animate({ r: 5 }, 500, "bounce");
27407                     this.label[1].attr({ "font-weight": 400 });
27408                 }
27409             };
27410
27411         switch(graphtype){
27412             case 'bar':
27413                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27414                 break;
27415             case 'hbar':
27416                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27417                 break;
27418             case 'pie':
27419 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27420 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27421 //            
27422                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27423                 
27424                 break;
27425
27426         }
27427         
27428         if(this.title){
27429             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27430         }
27431         
27432     },
27433     
27434     setTitle: function(o)
27435     {
27436         this.title = o;
27437     },
27438     
27439     initEvents: function() {
27440         
27441         if(!this.href){
27442             this.el.on('click', this.onClick, this);
27443         }
27444     },
27445     
27446     onClick : function(e)
27447     {
27448         Roo.log('img onclick');
27449         this.fireEvent('click', this, e);
27450     }
27451    
27452 });
27453
27454  
27455 /*
27456  * - LGPL
27457  *
27458  * numberBox
27459  * 
27460  */
27461 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27462
27463 /**
27464  * @class Roo.bootstrap.dash.NumberBox
27465  * @extends Roo.bootstrap.Component
27466  * Bootstrap NumberBox class
27467  * @cfg {String} headline Box headline
27468  * @cfg {String} content Box content
27469  * @cfg {String} icon Box icon
27470  * @cfg {String} footer Footer text
27471  * @cfg {String} fhref Footer href
27472  * 
27473  * @constructor
27474  * Create a new NumberBox
27475  * @param {Object} config The config object
27476  */
27477
27478
27479 Roo.bootstrap.dash.NumberBox = function(config){
27480     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27481     
27482 };
27483
27484 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27485     
27486     headline : '',
27487     content : '',
27488     icon : '',
27489     footer : '',
27490     fhref : '',
27491     ficon : '',
27492     
27493     getAutoCreate : function(){
27494         
27495         var cfg = {
27496             tag : 'div',
27497             cls : 'small-box ',
27498             cn : [
27499                 {
27500                     tag : 'div',
27501                     cls : 'inner',
27502                     cn :[
27503                         {
27504                             tag : 'h3',
27505                             cls : 'roo-headline',
27506                             html : this.headline
27507                         },
27508                         {
27509                             tag : 'p',
27510                             cls : 'roo-content',
27511                             html : this.content
27512                         }
27513                     ]
27514                 }
27515             ]
27516         };
27517         
27518         if(this.icon){
27519             cfg.cn.push({
27520                 tag : 'div',
27521                 cls : 'icon',
27522                 cn :[
27523                     {
27524                         tag : 'i',
27525                         cls : 'ion ' + this.icon
27526                     }
27527                 ]
27528             });
27529         }
27530         
27531         if(this.footer){
27532             var footer = {
27533                 tag : 'a',
27534                 cls : 'small-box-footer',
27535                 href : this.fhref || '#',
27536                 html : this.footer
27537             };
27538             
27539             cfg.cn.push(footer);
27540             
27541         }
27542         
27543         return  cfg;
27544     },
27545
27546     onRender : function(ct,position){
27547         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27548
27549
27550        
27551                 
27552     },
27553
27554     setHeadline: function (value)
27555     {
27556         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27557     },
27558     
27559     setFooter: function (value, href)
27560     {
27561         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27562         
27563         if(href){
27564             this.el.select('a.small-box-footer',true).first().attr('href', href);
27565         }
27566         
27567     },
27568
27569     setContent: function (value)
27570     {
27571         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27572     },
27573
27574     initEvents: function() 
27575     {   
27576         
27577     }
27578     
27579 });
27580
27581  
27582 /*
27583  * - LGPL
27584  *
27585  * TabBox
27586  * 
27587  */
27588 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27589
27590 /**
27591  * @class Roo.bootstrap.dash.TabBox
27592  * @extends Roo.bootstrap.Component
27593  * Bootstrap TabBox class
27594  * @cfg {String} title Title of the TabBox
27595  * @cfg {String} icon Icon of the TabBox
27596  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27597  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27598  * 
27599  * @constructor
27600  * Create a new TabBox
27601  * @param {Object} config The config object
27602  */
27603
27604
27605 Roo.bootstrap.dash.TabBox = function(config){
27606     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27607     this.addEvents({
27608         // raw events
27609         /**
27610          * @event addpane
27611          * When a pane is added
27612          * @param {Roo.bootstrap.dash.TabPane} pane
27613          */
27614         "addpane" : true,
27615         /**
27616          * @event activatepane
27617          * When a pane is activated
27618          * @param {Roo.bootstrap.dash.TabPane} pane
27619          */
27620         "activatepane" : true
27621         
27622          
27623     });
27624     
27625     this.panes = [];
27626 };
27627
27628 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27629
27630     title : '',
27631     icon : false,
27632     showtabs : true,
27633     tabScrollable : false,
27634     
27635     getChildContainer : function()
27636     {
27637         return this.el.select('.tab-content', true).first();
27638     },
27639     
27640     getAutoCreate : function(){
27641         
27642         var header = {
27643             tag: 'li',
27644             cls: 'pull-left header',
27645             html: this.title,
27646             cn : []
27647         };
27648         
27649         if(this.icon){
27650             header.cn.push({
27651                 tag: 'i',
27652                 cls: 'fa ' + this.icon
27653             });
27654         }
27655         
27656         var h = {
27657             tag: 'ul',
27658             cls: 'nav nav-tabs pull-right',
27659             cn: [
27660                 header
27661             ]
27662         };
27663         
27664         if(this.tabScrollable){
27665             h = {
27666                 tag: 'div',
27667                 cls: 'tab-header',
27668                 cn: [
27669                     {
27670                         tag: 'ul',
27671                         cls: 'nav nav-tabs pull-right',
27672                         cn: [
27673                             header
27674                         ]
27675                     }
27676                 ]
27677             };
27678         }
27679         
27680         var cfg = {
27681             tag: 'div',
27682             cls: 'nav-tabs-custom',
27683             cn: [
27684                 h,
27685                 {
27686                     tag: 'div',
27687                     cls: 'tab-content no-padding',
27688                     cn: []
27689                 }
27690             ]
27691         };
27692
27693         return  cfg;
27694     },
27695     initEvents : function()
27696     {
27697         //Roo.log('add add pane handler');
27698         this.on('addpane', this.onAddPane, this);
27699     },
27700      /**
27701      * Updates the box title
27702      * @param {String} html to set the title to.
27703      */
27704     setTitle : function(value)
27705     {
27706         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27707     },
27708     onAddPane : function(pane)
27709     {
27710         this.panes.push(pane);
27711         //Roo.log('addpane');
27712         //Roo.log(pane);
27713         // tabs are rendere left to right..
27714         if(!this.showtabs){
27715             return;
27716         }
27717         
27718         var ctr = this.el.select('.nav-tabs', true).first();
27719          
27720          
27721         var existing = ctr.select('.nav-tab',true);
27722         var qty = existing.getCount();;
27723         
27724         
27725         var tab = ctr.createChild({
27726             tag : 'li',
27727             cls : 'nav-tab' + (qty ? '' : ' active'),
27728             cn : [
27729                 {
27730                     tag : 'a',
27731                     href:'#',
27732                     html : pane.title
27733                 }
27734             ]
27735         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27736         pane.tab = tab;
27737         
27738         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27739         if (!qty) {
27740             pane.el.addClass('active');
27741         }
27742         
27743                 
27744     },
27745     onTabClick : function(ev,un,ob,pane)
27746     {
27747         //Roo.log('tab - prev default');
27748         ev.preventDefault();
27749         
27750         
27751         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27752         pane.tab.addClass('active');
27753         //Roo.log(pane.title);
27754         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27755         // technically we should have a deactivate event.. but maybe add later.
27756         // and it should not de-activate the selected tab...
27757         this.fireEvent('activatepane', pane);
27758         pane.el.addClass('active');
27759         pane.fireEvent('activate');
27760         
27761         
27762     },
27763     
27764     getActivePane : function()
27765     {
27766         var r = false;
27767         Roo.each(this.panes, function(p) {
27768             if(p.el.hasClass('active')){
27769                 r = p;
27770                 return false;
27771             }
27772             
27773             return;
27774         });
27775         
27776         return r;
27777     }
27778     
27779     
27780 });
27781
27782  
27783 /*
27784  * - LGPL
27785  *
27786  * Tab pane
27787  * 
27788  */
27789 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27790 /**
27791  * @class Roo.bootstrap.TabPane
27792  * @extends Roo.bootstrap.Component
27793  * Bootstrap TabPane class
27794  * @cfg {Boolean} active (false | true) Default false
27795  * @cfg {String} title title of panel
27796
27797  * 
27798  * @constructor
27799  * Create a new TabPane
27800  * @param {Object} config The config object
27801  */
27802
27803 Roo.bootstrap.dash.TabPane = function(config){
27804     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27805     
27806     this.addEvents({
27807         // raw events
27808         /**
27809          * @event activate
27810          * When a pane is activated
27811          * @param {Roo.bootstrap.dash.TabPane} pane
27812          */
27813         "activate" : true
27814          
27815     });
27816 };
27817
27818 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27819     
27820     active : false,
27821     title : '',
27822     
27823     // the tabBox that this is attached to.
27824     tab : false,
27825      
27826     getAutoCreate : function() 
27827     {
27828         var cfg = {
27829             tag: 'div',
27830             cls: 'tab-pane'
27831         };
27832         
27833         if(this.active){
27834             cfg.cls += ' active';
27835         }
27836         
27837         return cfg;
27838     },
27839     initEvents  : function()
27840     {
27841         //Roo.log('trigger add pane handler');
27842         this.parent().fireEvent('addpane', this)
27843     },
27844     
27845      /**
27846      * Updates the tab title 
27847      * @param {String} html to set the title to.
27848      */
27849     setTitle: function(str)
27850     {
27851         if (!this.tab) {
27852             return;
27853         }
27854         this.title = str;
27855         this.tab.select('a', true).first().dom.innerHTML = str;
27856         
27857     }
27858     
27859     
27860     
27861 });
27862
27863  
27864
27865
27866  /*
27867  * - LGPL
27868  *
27869  * menu
27870  * 
27871  */
27872 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27873
27874 /**
27875  * @class Roo.bootstrap.menu.Menu
27876  * @extends Roo.bootstrap.Component
27877  * Bootstrap Menu class - container for Menu
27878  * @cfg {String} html Text of the menu
27879  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27880  * @cfg {String} icon Font awesome icon
27881  * @cfg {String} pos Menu align to (top | bottom) default bottom
27882  * 
27883  * 
27884  * @constructor
27885  * Create a new Menu
27886  * @param {Object} config The config object
27887  */
27888
27889
27890 Roo.bootstrap.menu.Menu = function(config){
27891     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27892     
27893     this.addEvents({
27894         /**
27895          * @event beforeshow
27896          * Fires before this menu is displayed
27897          * @param {Roo.bootstrap.menu.Menu} this
27898          */
27899         beforeshow : true,
27900         /**
27901          * @event beforehide
27902          * Fires before this menu is hidden
27903          * @param {Roo.bootstrap.menu.Menu} this
27904          */
27905         beforehide : true,
27906         /**
27907          * @event show
27908          * Fires after this menu is displayed
27909          * @param {Roo.bootstrap.menu.Menu} this
27910          */
27911         show : true,
27912         /**
27913          * @event hide
27914          * Fires after this menu is hidden
27915          * @param {Roo.bootstrap.menu.Menu} this
27916          */
27917         hide : true,
27918         /**
27919          * @event click
27920          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27921          * @param {Roo.bootstrap.menu.Menu} this
27922          * @param {Roo.EventObject} e
27923          */
27924         click : true
27925     });
27926     
27927 };
27928
27929 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27930     
27931     submenu : false,
27932     html : '',
27933     weight : 'default',
27934     icon : false,
27935     pos : 'bottom',
27936     
27937     
27938     getChildContainer : function() {
27939         if(this.isSubMenu){
27940             return this.el;
27941         }
27942         
27943         return this.el.select('ul.dropdown-menu', true).first();  
27944     },
27945     
27946     getAutoCreate : function()
27947     {
27948         var text = [
27949             {
27950                 tag : 'span',
27951                 cls : 'roo-menu-text',
27952                 html : this.html
27953             }
27954         ];
27955         
27956         if(this.icon){
27957             text.unshift({
27958                 tag : 'i',
27959                 cls : 'fa ' + this.icon
27960             })
27961         }
27962         
27963         
27964         var cfg = {
27965             tag : 'div',
27966             cls : 'btn-group',
27967             cn : [
27968                 {
27969                     tag : 'button',
27970                     cls : 'dropdown-button btn btn-' + this.weight,
27971                     cn : text
27972                 },
27973                 {
27974                     tag : 'button',
27975                     cls : 'dropdown-toggle btn btn-' + this.weight,
27976                     cn : [
27977                         {
27978                             tag : 'span',
27979                             cls : 'caret'
27980                         }
27981                     ]
27982                 },
27983                 {
27984                     tag : 'ul',
27985                     cls : 'dropdown-menu'
27986                 }
27987             ]
27988             
27989         };
27990         
27991         if(this.pos == 'top'){
27992             cfg.cls += ' dropup';
27993         }
27994         
27995         if(this.isSubMenu){
27996             cfg = {
27997                 tag : 'ul',
27998                 cls : 'dropdown-menu'
27999             }
28000         }
28001         
28002         return cfg;
28003     },
28004     
28005     onRender : function(ct, position)
28006     {
28007         this.isSubMenu = ct.hasClass('dropdown-submenu');
28008         
28009         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28010     },
28011     
28012     initEvents : function() 
28013     {
28014         if(this.isSubMenu){
28015             return;
28016         }
28017         
28018         this.hidden = true;
28019         
28020         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28021         this.triggerEl.on('click', this.onTriggerPress, this);
28022         
28023         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28024         this.buttonEl.on('click', this.onClick, this);
28025         
28026     },
28027     
28028     list : function()
28029     {
28030         if(this.isSubMenu){
28031             return this.el;
28032         }
28033         
28034         return this.el.select('ul.dropdown-menu', true).first();
28035     },
28036     
28037     onClick : function(e)
28038     {
28039         this.fireEvent("click", this, e);
28040     },
28041     
28042     onTriggerPress  : function(e)
28043     {   
28044         if (this.isVisible()) {
28045             this.hide();
28046         } else {
28047             this.show();
28048         }
28049     },
28050     
28051     isVisible : function(){
28052         return !this.hidden;
28053     },
28054     
28055     show : function()
28056     {
28057         this.fireEvent("beforeshow", this);
28058         
28059         this.hidden = false;
28060         this.el.addClass('open');
28061         
28062         Roo.get(document).on("mouseup", this.onMouseUp, this);
28063         
28064         this.fireEvent("show", this);
28065         
28066         
28067     },
28068     
28069     hide : function()
28070     {
28071         this.fireEvent("beforehide", this);
28072         
28073         this.hidden = true;
28074         this.el.removeClass('open');
28075         
28076         Roo.get(document).un("mouseup", this.onMouseUp);
28077         
28078         this.fireEvent("hide", this);
28079     },
28080     
28081     onMouseUp : function()
28082     {
28083         this.hide();
28084     }
28085     
28086 });
28087
28088  
28089  /*
28090  * - LGPL
28091  *
28092  * menu item
28093  * 
28094  */
28095 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28096
28097 /**
28098  * @class Roo.bootstrap.menu.Item
28099  * @extends Roo.bootstrap.Component
28100  * Bootstrap MenuItem class
28101  * @cfg {Boolean} submenu (true | false) default false
28102  * @cfg {String} html text of the item
28103  * @cfg {String} href the link
28104  * @cfg {Boolean} disable (true | false) default false
28105  * @cfg {Boolean} preventDefault (true | false) default true
28106  * @cfg {String} icon Font awesome icon
28107  * @cfg {String} pos Submenu align to (left | right) default right 
28108  * 
28109  * 
28110  * @constructor
28111  * Create a new Item
28112  * @param {Object} config The config object
28113  */
28114
28115
28116 Roo.bootstrap.menu.Item = function(config){
28117     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28118     this.addEvents({
28119         /**
28120          * @event mouseover
28121          * Fires when the mouse is hovering over this menu
28122          * @param {Roo.bootstrap.menu.Item} this
28123          * @param {Roo.EventObject} e
28124          */
28125         mouseover : true,
28126         /**
28127          * @event mouseout
28128          * Fires when the mouse exits this menu
28129          * @param {Roo.bootstrap.menu.Item} this
28130          * @param {Roo.EventObject} e
28131          */
28132         mouseout : true,
28133         // raw events
28134         /**
28135          * @event click
28136          * The raw click event for the entire grid.
28137          * @param {Roo.EventObject} e
28138          */
28139         click : true
28140     });
28141 };
28142
28143 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28144     
28145     submenu : false,
28146     href : '',
28147     html : '',
28148     preventDefault: true,
28149     disable : false,
28150     icon : false,
28151     pos : 'right',
28152     
28153     getAutoCreate : function()
28154     {
28155         var text = [
28156             {
28157                 tag : 'span',
28158                 cls : 'roo-menu-item-text',
28159                 html : this.html
28160             }
28161         ];
28162         
28163         if(this.icon){
28164             text.unshift({
28165                 tag : 'i',
28166                 cls : 'fa ' + this.icon
28167             })
28168         }
28169         
28170         var cfg = {
28171             tag : 'li',
28172             cn : [
28173                 {
28174                     tag : 'a',
28175                     href : this.href || '#',
28176                     cn : text
28177                 }
28178             ]
28179         };
28180         
28181         if(this.disable){
28182             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28183         }
28184         
28185         if(this.submenu){
28186             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28187             
28188             if(this.pos == 'left'){
28189                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28190             }
28191         }
28192         
28193         return cfg;
28194     },
28195     
28196     initEvents : function() 
28197     {
28198         this.el.on('mouseover', this.onMouseOver, this);
28199         this.el.on('mouseout', this.onMouseOut, this);
28200         
28201         this.el.select('a', true).first().on('click', this.onClick, this);
28202         
28203     },
28204     
28205     onClick : function(e)
28206     {
28207         if(this.preventDefault){
28208             e.preventDefault();
28209         }
28210         
28211         this.fireEvent("click", this, e);
28212     },
28213     
28214     onMouseOver : function(e)
28215     {
28216         if(this.submenu && this.pos == 'left'){
28217             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28218         }
28219         
28220         this.fireEvent("mouseover", this, e);
28221     },
28222     
28223     onMouseOut : function(e)
28224     {
28225         this.fireEvent("mouseout", this, e);
28226     }
28227 });
28228
28229  
28230
28231  /*
28232  * - LGPL
28233  *
28234  * menu separator
28235  * 
28236  */
28237 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28238
28239 /**
28240  * @class Roo.bootstrap.menu.Separator
28241  * @extends Roo.bootstrap.Component
28242  * Bootstrap Separator class
28243  * 
28244  * @constructor
28245  * Create a new Separator
28246  * @param {Object} config The config object
28247  */
28248
28249
28250 Roo.bootstrap.menu.Separator = function(config){
28251     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28252 };
28253
28254 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28255     
28256     getAutoCreate : function(){
28257         var cfg = {
28258             tag : 'li',
28259             cls: 'divider'
28260         };
28261         
28262         return cfg;
28263     }
28264    
28265 });
28266
28267  
28268
28269  /*
28270  * - LGPL
28271  *
28272  * Tooltip
28273  * 
28274  */
28275
28276 /**
28277  * @class Roo.bootstrap.Tooltip
28278  * Bootstrap Tooltip class
28279  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28280  * to determine which dom element triggers the tooltip.
28281  * 
28282  * It needs to add support for additional attributes like tooltip-position
28283  * 
28284  * @constructor
28285  * Create a new Toolti
28286  * @param {Object} config The config object
28287  */
28288
28289 Roo.bootstrap.Tooltip = function(config){
28290     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28291     
28292     this.alignment = Roo.bootstrap.Tooltip.alignment;
28293     
28294     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28295         this.alignment = config.alignment;
28296     }
28297     
28298 };
28299
28300 Roo.apply(Roo.bootstrap.Tooltip, {
28301     /**
28302      * @function init initialize tooltip monitoring.
28303      * @static
28304      */
28305     currentEl : false,
28306     currentTip : false,
28307     currentRegion : false,
28308     
28309     //  init : delay?
28310     
28311     init : function()
28312     {
28313         Roo.get(document).on('mouseover', this.enter ,this);
28314         Roo.get(document).on('mouseout', this.leave, this);
28315          
28316         
28317         this.currentTip = new Roo.bootstrap.Tooltip();
28318     },
28319     
28320     enter : function(ev)
28321     {
28322         var dom = ev.getTarget();
28323         
28324         //Roo.log(['enter',dom]);
28325         var el = Roo.fly(dom);
28326         if (this.currentEl) {
28327             //Roo.log(dom);
28328             //Roo.log(this.currentEl);
28329             //Roo.log(this.currentEl.contains(dom));
28330             if (this.currentEl == el) {
28331                 return;
28332             }
28333             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28334                 return;
28335             }
28336
28337         }
28338         
28339         if (this.currentTip.el) {
28340             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28341         }    
28342         //Roo.log(ev);
28343         
28344         if(!el || el.dom == document){
28345             return;
28346         }
28347         
28348         var bindEl = el;
28349         
28350         // you can not look for children, as if el is the body.. then everythign is the child..
28351         if (!el.attr('tooltip')) { //
28352             if (!el.select("[tooltip]").elements.length) {
28353                 return;
28354             }
28355             // is the mouse over this child...?
28356             bindEl = el.select("[tooltip]").first();
28357             var xy = ev.getXY();
28358             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28359                 //Roo.log("not in region.");
28360                 return;
28361             }
28362             //Roo.log("child element over..");
28363             
28364         }
28365         this.currentEl = bindEl;
28366         this.currentTip.bind(bindEl);
28367         this.currentRegion = Roo.lib.Region.getRegion(dom);
28368         this.currentTip.enter();
28369         
28370     },
28371     leave : function(ev)
28372     {
28373         var dom = ev.getTarget();
28374         //Roo.log(['leave',dom]);
28375         if (!this.currentEl) {
28376             return;
28377         }
28378         
28379         
28380         if (dom != this.currentEl.dom) {
28381             return;
28382         }
28383         var xy = ev.getXY();
28384         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28385             return;
28386         }
28387         // only activate leave if mouse cursor is outside... bounding box..
28388         
28389         
28390         
28391         
28392         if (this.currentTip) {
28393             this.currentTip.leave();
28394         }
28395         //Roo.log('clear currentEl');
28396         this.currentEl = false;
28397         
28398         
28399     },
28400     alignment : {
28401         'left' : ['r-l', [-2,0], 'right'],
28402         'right' : ['l-r', [2,0], 'left'],
28403         'bottom' : ['t-b', [0,2], 'top'],
28404         'top' : [ 'b-t', [0,-2], 'bottom']
28405     }
28406     
28407 });
28408
28409
28410 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28411     
28412     
28413     bindEl : false,
28414     
28415     delay : null, // can be { show : 300 , hide: 500}
28416     
28417     timeout : null,
28418     
28419     hoverState : null, //???
28420     
28421     placement : 'bottom', 
28422     
28423     alignment : false,
28424     
28425     getAutoCreate : function(){
28426     
28427         var cfg = {
28428            cls : 'tooltip',   
28429            role : 'tooltip',
28430            cn : [
28431                 {
28432                     cls : 'tooltip-arrow arrow'
28433                 },
28434                 {
28435                     cls : 'tooltip-inner'
28436                 }
28437            ]
28438         };
28439         
28440         return cfg;
28441     },
28442     bind : function(el)
28443     {
28444         this.bindEl = el;
28445     },
28446     
28447     initEvents : function()
28448     {
28449         this.arrowEl = this.el.select('.arrow', true).first();
28450         this.innerEl = this.el.select('.tooltip-inner', true).first();
28451     },
28452     
28453     enter : function () {
28454        
28455         if (this.timeout != null) {
28456             clearTimeout(this.timeout);
28457         }
28458         
28459         this.hoverState = 'in';
28460          //Roo.log("enter - show");
28461         if (!this.delay || !this.delay.show) {
28462             this.show();
28463             return;
28464         }
28465         var _t = this;
28466         this.timeout = setTimeout(function () {
28467             if (_t.hoverState == 'in') {
28468                 _t.show();
28469             }
28470         }, this.delay.show);
28471     },
28472     leave : function()
28473     {
28474         clearTimeout(this.timeout);
28475     
28476         this.hoverState = 'out';
28477          if (!this.delay || !this.delay.hide) {
28478             this.hide();
28479             return;
28480         }
28481        
28482         var _t = this;
28483         this.timeout = setTimeout(function () {
28484             //Roo.log("leave - timeout");
28485             
28486             if (_t.hoverState == 'out') {
28487                 _t.hide();
28488                 Roo.bootstrap.Tooltip.currentEl = false;
28489             }
28490         }, delay);
28491     },
28492     
28493     show : function (msg)
28494     {
28495         if (!this.el) {
28496             this.render(document.body);
28497         }
28498         // set content.
28499         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28500         
28501         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28502         
28503         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28504         
28505         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28506                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28507         
28508         var placement = typeof this.placement == 'function' ?
28509             this.placement.call(this, this.el, on_el) :
28510             this.placement;
28511             
28512         var autoToken = /\s?auto?\s?/i;
28513         var autoPlace = autoToken.test(placement);
28514         if (autoPlace) {
28515             placement = placement.replace(autoToken, '') || 'top';
28516         }
28517         
28518         //this.el.detach()
28519         //this.el.setXY([0,0]);
28520         this.el.show();
28521         //this.el.dom.style.display='block';
28522         
28523         //this.el.appendTo(on_el);
28524         
28525         var p = this.getPosition();
28526         var box = this.el.getBox();
28527         
28528         if (autoPlace) {
28529             // fixme..
28530         }
28531         
28532         var align = this.alignment[placement];
28533         
28534         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28535         
28536         if(placement == 'top' || placement == 'bottom'){
28537             if(xy[0] < 0){
28538                 placement = 'right';
28539             }
28540             
28541             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28542                 placement = 'left';
28543             }
28544             
28545             var scroll = Roo.select('body', true).first().getScroll();
28546             
28547             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28548                 placement = 'top';
28549             }
28550             
28551             align = this.alignment[placement];
28552             
28553             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28554             
28555         }
28556         
28557         this.el.alignTo(this.bindEl, align[0],align[1]);
28558         //var arrow = this.el.select('.arrow',true).first();
28559         //arrow.set(align[2], 
28560         
28561         this.el.addClass(placement);
28562         this.el.addClass("bs-tooltip-"+ placement);
28563         
28564         this.el.addClass('in fade show');
28565         
28566         this.hoverState = null;
28567         
28568         if (this.el.hasClass('fade')) {
28569             // fade it?
28570         }
28571         
28572         
28573         
28574         
28575         
28576     },
28577     hide : function()
28578     {
28579          
28580         if (!this.el) {
28581             return;
28582         }
28583         //this.el.setXY([0,0]);
28584         this.el.removeClass(['show', 'in']);
28585         //this.el.hide();
28586         
28587     }
28588     
28589 });
28590  
28591
28592  /*
28593  * - LGPL
28594  *
28595  * Location Picker
28596  * 
28597  */
28598
28599 /**
28600  * @class Roo.bootstrap.LocationPicker
28601  * @extends Roo.bootstrap.Component
28602  * Bootstrap LocationPicker class
28603  * @cfg {Number} latitude Position when init default 0
28604  * @cfg {Number} longitude Position when init default 0
28605  * @cfg {Number} zoom default 15
28606  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28607  * @cfg {Boolean} mapTypeControl default false
28608  * @cfg {Boolean} disableDoubleClickZoom default false
28609  * @cfg {Boolean} scrollwheel default true
28610  * @cfg {Boolean} streetViewControl default false
28611  * @cfg {Number} radius default 0
28612  * @cfg {String} locationName
28613  * @cfg {Boolean} draggable default true
28614  * @cfg {Boolean} enableAutocomplete default false
28615  * @cfg {Boolean} enableReverseGeocode default true
28616  * @cfg {String} markerTitle
28617  * 
28618  * @constructor
28619  * Create a new LocationPicker
28620  * @param {Object} config The config object
28621  */
28622
28623
28624 Roo.bootstrap.LocationPicker = function(config){
28625     
28626     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28627     
28628     this.addEvents({
28629         /**
28630          * @event initial
28631          * Fires when the picker initialized.
28632          * @param {Roo.bootstrap.LocationPicker} this
28633          * @param {Google Location} location
28634          */
28635         initial : true,
28636         /**
28637          * @event positionchanged
28638          * Fires when the picker position changed.
28639          * @param {Roo.bootstrap.LocationPicker} this
28640          * @param {Google Location} location
28641          */
28642         positionchanged : true,
28643         /**
28644          * @event resize
28645          * Fires when the map resize.
28646          * @param {Roo.bootstrap.LocationPicker} this
28647          */
28648         resize : true,
28649         /**
28650          * @event show
28651          * Fires when the map show.
28652          * @param {Roo.bootstrap.LocationPicker} this
28653          */
28654         show : true,
28655         /**
28656          * @event hide
28657          * Fires when the map hide.
28658          * @param {Roo.bootstrap.LocationPicker} this
28659          */
28660         hide : true,
28661         /**
28662          * @event mapClick
28663          * Fires when click the map.
28664          * @param {Roo.bootstrap.LocationPicker} this
28665          * @param {Map event} e
28666          */
28667         mapClick : true,
28668         /**
28669          * @event mapRightClick
28670          * Fires when right click the map.
28671          * @param {Roo.bootstrap.LocationPicker} this
28672          * @param {Map event} e
28673          */
28674         mapRightClick : true,
28675         /**
28676          * @event markerClick
28677          * Fires when click the marker.
28678          * @param {Roo.bootstrap.LocationPicker} this
28679          * @param {Map event} e
28680          */
28681         markerClick : true,
28682         /**
28683          * @event markerRightClick
28684          * Fires when right click the marker.
28685          * @param {Roo.bootstrap.LocationPicker} this
28686          * @param {Map event} e
28687          */
28688         markerRightClick : true,
28689         /**
28690          * @event OverlayViewDraw
28691          * Fires when OverlayView Draw
28692          * @param {Roo.bootstrap.LocationPicker} this
28693          */
28694         OverlayViewDraw : true,
28695         /**
28696          * @event OverlayViewOnAdd
28697          * Fires when OverlayView Draw
28698          * @param {Roo.bootstrap.LocationPicker} this
28699          */
28700         OverlayViewOnAdd : true,
28701         /**
28702          * @event OverlayViewOnRemove
28703          * Fires when OverlayView Draw
28704          * @param {Roo.bootstrap.LocationPicker} this
28705          */
28706         OverlayViewOnRemove : true,
28707         /**
28708          * @event OverlayViewShow
28709          * Fires when OverlayView Draw
28710          * @param {Roo.bootstrap.LocationPicker} this
28711          * @param {Pixel} cpx
28712          */
28713         OverlayViewShow : true,
28714         /**
28715          * @event OverlayViewHide
28716          * Fires when OverlayView Draw
28717          * @param {Roo.bootstrap.LocationPicker} this
28718          */
28719         OverlayViewHide : true,
28720         /**
28721          * @event loadexception
28722          * Fires when load google lib failed.
28723          * @param {Roo.bootstrap.LocationPicker} this
28724          */
28725         loadexception : true
28726     });
28727         
28728 };
28729
28730 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28731     
28732     gMapContext: false,
28733     
28734     latitude: 0,
28735     longitude: 0,
28736     zoom: 15,
28737     mapTypeId: false,
28738     mapTypeControl: false,
28739     disableDoubleClickZoom: false,
28740     scrollwheel: true,
28741     streetViewControl: false,
28742     radius: 0,
28743     locationName: '',
28744     draggable: true,
28745     enableAutocomplete: false,
28746     enableReverseGeocode: true,
28747     markerTitle: '',
28748     
28749     getAutoCreate: function()
28750     {
28751
28752         var cfg = {
28753             tag: 'div',
28754             cls: 'roo-location-picker'
28755         };
28756         
28757         return cfg
28758     },
28759     
28760     initEvents: function(ct, position)
28761     {       
28762         if(!this.el.getWidth() || this.isApplied()){
28763             return;
28764         }
28765         
28766         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28767         
28768         this.initial();
28769     },
28770     
28771     initial: function()
28772     {
28773         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28774             this.fireEvent('loadexception', this);
28775             return;
28776         }
28777         
28778         if(!this.mapTypeId){
28779             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28780         }
28781         
28782         this.gMapContext = this.GMapContext();
28783         
28784         this.initOverlayView();
28785         
28786         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28787         
28788         var _this = this;
28789                 
28790         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28791             _this.setPosition(_this.gMapContext.marker.position);
28792         });
28793         
28794         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28795             _this.fireEvent('mapClick', this, event);
28796             
28797         });
28798
28799         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28800             _this.fireEvent('mapRightClick', this, event);
28801             
28802         });
28803         
28804         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28805             _this.fireEvent('markerClick', this, event);
28806             
28807         });
28808
28809         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28810             _this.fireEvent('markerRightClick', this, event);
28811             
28812         });
28813         
28814         this.setPosition(this.gMapContext.location);
28815         
28816         this.fireEvent('initial', this, this.gMapContext.location);
28817     },
28818     
28819     initOverlayView: function()
28820     {
28821         var _this = this;
28822         
28823         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28824             
28825             draw: function()
28826             {
28827                 _this.fireEvent('OverlayViewDraw', _this);
28828             },
28829             
28830             onAdd: function()
28831             {
28832                 _this.fireEvent('OverlayViewOnAdd', _this);
28833             },
28834             
28835             onRemove: function()
28836             {
28837                 _this.fireEvent('OverlayViewOnRemove', _this);
28838             },
28839             
28840             show: function(cpx)
28841             {
28842                 _this.fireEvent('OverlayViewShow', _this, cpx);
28843             },
28844             
28845             hide: function()
28846             {
28847                 _this.fireEvent('OverlayViewHide', _this);
28848             }
28849             
28850         });
28851     },
28852     
28853     fromLatLngToContainerPixel: function(event)
28854     {
28855         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28856     },
28857     
28858     isApplied: function() 
28859     {
28860         return this.getGmapContext() == false ? false : true;
28861     },
28862     
28863     getGmapContext: function() 
28864     {
28865         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28866     },
28867     
28868     GMapContext: function() 
28869     {
28870         var position = new google.maps.LatLng(this.latitude, this.longitude);
28871         
28872         var _map = new google.maps.Map(this.el.dom, {
28873             center: position,
28874             zoom: this.zoom,
28875             mapTypeId: this.mapTypeId,
28876             mapTypeControl: this.mapTypeControl,
28877             disableDoubleClickZoom: this.disableDoubleClickZoom,
28878             scrollwheel: this.scrollwheel,
28879             streetViewControl: this.streetViewControl,
28880             locationName: this.locationName,
28881             draggable: this.draggable,
28882             enableAutocomplete: this.enableAutocomplete,
28883             enableReverseGeocode: this.enableReverseGeocode
28884         });
28885         
28886         var _marker = new google.maps.Marker({
28887             position: position,
28888             map: _map,
28889             title: this.markerTitle,
28890             draggable: this.draggable
28891         });
28892         
28893         return {
28894             map: _map,
28895             marker: _marker,
28896             circle: null,
28897             location: position,
28898             radius: this.radius,
28899             locationName: this.locationName,
28900             addressComponents: {
28901                 formatted_address: null,
28902                 addressLine1: null,
28903                 addressLine2: null,
28904                 streetName: null,
28905                 streetNumber: null,
28906                 city: null,
28907                 district: null,
28908                 state: null,
28909                 stateOrProvince: null
28910             },
28911             settings: this,
28912             domContainer: this.el.dom,
28913             geodecoder: new google.maps.Geocoder()
28914         };
28915     },
28916     
28917     drawCircle: function(center, radius, options) 
28918     {
28919         if (this.gMapContext.circle != null) {
28920             this.gMapContext.circle.setMap(null);
28921         }
28922         if (radius > 0) {
28923             radius *= 1;
28924             options = Roo.apply({}, options, {
28925                 strokeColor: "#0000FF",
28926                 strokeOpacity: .35,
28927                 strokeWeight: 2,
28928                 fillColor: "#0000FF",
28929                 fillOpacity: .2
28930             });
28931             
28932             options.map = this.gMapContext.map;
28933             options.radius = radius;
28934             options.center = center;
28935             this.gMapContext.circle = new google.maps.Circle(options);
28936             return this.gMapContext.circle;
28937         }
28938         
28939         return null;
28940     },
28941     
28942     setPosition: function(location) 
28943     {
28944         this.gMapContext.location = location;
28945         this.gMapContext.marker.setPosition(location);
28946         this.gMapContext.map.panTo(location);
28947         this.drawCircle(location, this.gMapContext.radius, {});
28948         
28949         var _this = this;
28950         
28951         if (this.gMapContext.settings.enableReverseGeocode) {
28952             this.gMapContext.geodecoder.geocode({
28953                 latLng: this.gMapContext.location
28954             }, function(results, status) {
28955                 
28956                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28957                     _this.gMapContext.locationName = results[0].formatted_address;
28958                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28959                     
28960                     _this.fireEvent('positionchanged', this, location);
28961                 }
28962             });
28963             
28964             return;
28965         }
28966         
28967         this.fireEvent('positionchanged', this, location);
28968     },
28969     
28970     resize: function()
28971     {
28972         google.maps.event.trigger(this.gMapContext.map, "resize");
28973         
28974         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28975         
28976         this.fireEvent('resize', this);
28977     },
28978     
28979     setPositionByLatLng: function(latitude, longitude)
28980     {
28981         this.setPosition(new google.maps.LatLng(latitude, longitude));
28982     },
28983     
28984     getCurrentPosition: function() 
28985     {
28986         return {
28987             latitude: this.gMapContext.location.lat(),
28988             longitude: this.gMapContext.location.lng()
28989         };
28990     },
28991     
28992     getAddressName: function() 
28993     {
28994         return this.gMapContext.locationName;
28995     },
28996     
28997     getAddressComponents: function() 
28998     {
28999         return this.gMapContext.addressComponents;
29000     },
29001     
29002     address_component_from_google_geocode: function(address_components) 
29003     {
29004         var result = {};
29005         
29006         for (var i = 0; i < address_components.length; i++) {
29007             var component = address_components[i];
29008             if (component.types.indexOf("postal_code") >= 0) {
29009                 result.postalCode = component.short_name;
29010             } else if (component.types.indexOf("street_number") >= 0) {
29011                 result.streetNumber = component.short_name;
29012             } else if (component.types.indexOf("route") >= 0) {
29013                 result.streetName = component.short_name;
29014             } else if (component.types.indexOf("neighborhood") >= 0) {
29015                 result.city = component.short_name;
29016             } else if (component.types.indexOf("locality") >= 0) {
29017                 result.city = component.short_name;
29018             } else if (component.types.indexOf("sublocality") >= 0) {
29019                 result.district = component.short_name;
29020             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29021                 result.stateOrProvince = component.short_name;
29022             } else if (component.types.indexOf("country") >= 0) {
29023                 result.country = component.short_name;
29024             }
29025         }
29026         
29027         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29028         result.addressLine2 = "";
29029         return result;
29030     },
29031     
29032     setZoomLevel: function(zoom)
29033     {
29034         this.gMapContext.map.setZoom(zoom);
29035     },
29036     
29037     show: function()
29038     {
29039         if(!this.el){
29040             return;
29041         }
29042         
29043         this.el.show();
29044         
29045         this.resize();
29046         
29047         this.fireEvent('show', this);
29048     },
29049     
29050     hide: function()
29051     {
29052         if(!this.el){
29053             return;
29054         }
29055         
29056         this.el.hide();
29057         
29058         this.fireEvent('hide', this);
29059     }
29060     
29061 });
29062
29063 Roo.apply(Roo.bootstrap.LocationPicker, {
29064     
29065     OverlayView : function(map, options)
29066     {
29067         options = options || {};
29068         
29069         this.setMap(map);
29070     }
29071     
29072     
29073 });/**
29074  * @class Roo.bootstrap.Alert
29075  * @extends Roo.bootstrap.Component
29076  * Bootstrap Alert class - shows an alert area box
29077  * eg
29078  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29079   Enter a valid email address
29080 </div>
29081  * @licence LGPL
29082  * @cfg {String} title The title of alert
29083  * @cfg {String} html The content of alert
29084  * @cfg {String} weight (  success | info | warning | danger )
29085  * @cfg {String} faicon font-awesomeicon
29086  * 
29087  * @constructor
29088  * Create a new alert
29089  * @param {Object} config The config object
29090  */
29091
29092
29093 Roo.bootstrap.Alert = function(config){
29094     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29095     
29096 };
29097
29098 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29099     
29100     title: '',
29101     html: '',
29102     weight: false,
29103     faicon: false,
29104     
29105     getAutoCreate : function()
29106     {
29107         
29108         var cfg = {
29109             tag : 'div',
29110             cls : 'alert',
29111             cn : [
29112                 {
29113                     tag : 'i',
29114                     cls : 'roo-alert-icon'
29115                     
29116                 },
29117                 {
29118                     tag : 'b',
29119                     cls : 'roo-alert-title',
29120                     html : this.title
29121                 },
29122                 {
29123                     tag : 'span',
29124                     cls : 'roo-alert-text',
29125                     html : this.html
29126                 }
29127             ]
29128         };
29129         
29130         if(this.faicon){
29131             cfg.cn[0].cls += ' fa ' + this.faicon;
29132         }
29133         
29134         if(this.weight){
29135             cfg.cls += ' alert-' + this.weight;
29136         }
29137         
29138         return cfg;
29139     },
29140     
29141     initEvents: function() 
29142     {
29143         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29144     },
29145     
29146     setTitle : function(str)
29147     {
29148         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29149     },
29150     
29151     setText : function(str)
29152     {
29153         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29154     },
29155     
29156     setWeight : function(weight)
29157     {
29158         if(this.weight){
29159             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29160         }
29161         
29162         this.weight = weight;
29163         
29164         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29165     },
29166     
29167     setIcon : function(icon)
29168     {
29169         if(this.faicon){
29170             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29171         }
29172         
29173         this.faicon = icon;
29174         
29175         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29176     },
29177     
29178     hide: function() 
29179     {
29180         this.el.hide();   
29181     },
29182     
29183     show: function() 
29184     {  
29185         this.el.show();   
29186     }
29187     
29188 });
29189
29190  
29191 /*
29192 * Licence: LGPL
29193 */
29194
29195 /**
29196  * @class Roo.bootstrap.UploadCropbox
29197  * @extends Roo.bootstrap.Component
29198  * Bootstrap UploadCropbox class
29199  * @cfg {String} emptyText show when image has been loaded
29200  * @cfg {String} rotateNotify show when image too small to rotate
29201  * @cfg {Number} errorTimeout default 3000
29202  * @cfg {Number} minWidth default 300
29203  * @cfg {Number} minHeight default 300
29204  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29205  * @cfg {Boolean} isDocument (true|false) default false
29206  * @cfg {String} url action url
29207  * @cfg {String} paramName default 'imageUpload'
29208  * @cfg {String} method default POST
29209  * @cfg {Boolean} loadMask (true|false) default true
29210  * @cfg {Boolean} loadingText default 'Loading...'
29211  * 
29212  * @constructor
29213  * Create a new UploadCropbox
29214  * @param {Object} config The config object
29215  */
29216
29217 Roo.bootstrap.UploadCropbox = function(config){
29218     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29219     
29220     this.addEvents({
29221         /**
29222          * @event beforeselectfile
29223          * Fire before select file
29224          * @param {Roo.bootstrap.UploadCropbox} this
29225          */
29226         "beforeselectfile" : true,
29227         /**
29228          * @event initial
29229          * Fire after initEvent
29230          * @param {Roo.bootstrap.UploadCropbox} this
29231          */
29232         "initial" : true,
29233         /**
29234          * @event crop
29235          * Fire after initEvent
29236          * @param {Roo.bootstrap.UploadCropbox} this
29237          * @param {String} data
29238          */
29239         "crop" : true,
29240         /**
29241          * @event prepare
29242          * Fire when preparing the file data
29243          * @param {Roo.bootstrap.UploadCropbox} this
29244          * @param {Object} file
29245          */
29246         "prepare" : true,
29247         /**
29248          * @event exception
29249          * Fire when get exception
29250          * @param {Roo.bootstrap.UploadCropbox} this
29251          * @param {XMLHttpRequest} xhr
29252          */
29253         "exception" : true,
29254         /**
29255          * @event beforeloadcanvas
29256          * Fire before load the canvas
29257          * @param {Roo.bootstrap.UploadCropbox} this
29258          * @param {String} src
29259          */
29260         "beforeloadcanvas" : true,
29261         /**
29262          * @event trash
29263          * Fire when trash image
29264          * @param {Roo.bootstrap.UploadCropbox} this
29265          */
29266         "trash" : true,
29267         /**
29268          * @event download
29269          * Fire when download the image
29270          * @param {Roo.bootstrap.UploadCropbox} this
29271          */
29272         "download" : true,
29273         /**
29274          * @event footerbuttonclick
29275          * Fire when footerbuttonclick
29276          * @param {Roo.bootstrap.UploadCropbox} this
29277          * @param {String} type
29278          */
29279         "footerbuttonclick" : true,
29280         /**
29281          * @event resize
29282          * Fire when resize
29283          * @param {Roo.bootstrap.UploadCropbox} this
29284          */
29285         "resize" : true,
29286         /**
29287          * @event rotate
29288          * Fire when rotate the image
29289          * @param {Roo.bootstrap.UploadCropbox} this
29290          * @param {String} pos
29291          */
29292         "rotate" : true,
29293         /**
29294          * @event inspect
29295          * Fire when inspect the file
29296          * @param {Roo.bootstrap.UploadCropbox} this
29297          * @param {Object} file
29298          */
29299         "inspect" : true,
29300         /**
29301          * @event upload
29302          * Fire when xhr upload the file
29303          * @param {Roo.bootstrap.UploadCropbox} this
29304          * @param {Object} data
29305          */
29306         "upload" : true,
29307         /**
29308          * @event arrange
29309          * Fire when arrange the file data
29310          * @param {Roo.bootstrap.UploadCropbox} this
29311          * @param {Object} formData
29312          */
29313         "arrange" : true
29314     });
29315     
29316     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29317 };
29318
29319 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29320     
29321     emptyText : 'Click to upload image',
29322     rotateNotify : 'Image is too small to rotate',
29323     errorTimeout : 3000,
29324     scale : 0,
29325     baseScale : 1,
29326     rotate : 0,
29327     dragable : false,
29328     pinching : false,
29329     mouseX : 0,
29330     mouseY : 0,
29331     cropData : false,
29332     minWidth : 300,
29333     minHeight : 300,
29334     file : false,
29335     exif : {},
29336     baseRotate : 1,
29337     cropType : 'image/jpeg',
29338     buttons : false,
29339     canvasLoaded : false,
29340     isDocument : false,
29341     method : 'POST',
29342     paramName : 'imageUpload',
29343     loadMask : true,
29344     loadingText : 'Loading...',
29345     maskEl : false,
29346     
29347     getAutoCreate : function()
29348     {
29349         var cfg = {
29350             tag : 'div',
29351             cls : 'roo-upload-cropbox',
29352             cn : [
29353                 {
29354                     tag : 'input',
29355                     cls : 'roo-upload-cropbox-selector',
29356                     type : 'file'
29357                 },
29358                 {
29359                     tag : 'div',
29360                     cls : 'roo-upload-cropbox-body',
29361                     style : 'cursor:pointer',
29362                     cn : [
29363                         {
29364                             tag : 'div',
29365                             cls : 'roo-upload-cropbox-preview'
29366                         },
29367                         {
29368                             tag : 'div',
29369                             cls : 'roo-upload-cropbox-thumb'
29370                         },
29371                         {
29372                             tag : 'div',
29373                             cls : 'roo-upload-cropbox-empty-notify',
29374                             html : this.emptyText
29375                         },
29376                         {
29377                             tag : 'div',
29378                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29379                             html : this.rotateNotify
29380                         }
29381                     ]
29382                 },
29383                 {
29384                     tag : 'div',
29385                     cls : 'roo-upload-cropbox-footer',
29386                     cn : {
29387                         tag : 'div',
29388                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29389                         cn : []
29390                     }
29391                 }
29392             ]
29393         };
29394         
29395         return cfg;
29396     },
29397     
29398     onRender : function(ct, position)
29399     {
29400         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29401         
29402         if (this.buttons.length) {
29403             
29404             Roo.each(this.buttons, function(bb) {
29405                 
29406                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29407                 
29408                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29409                 
29410             }, this);
29411         }
29412         
29413         if(this.loadMask){
29414             this.maskEl = this.el;
29415         }
29416     },
29417     
29418     initEvents : function()
29419     {
29420         this.urlAPI = (window.createObjectURL && window) || 
29421                                 (window.URL && URL.revokeObjectURL && URL) || 
29422                                 (window.webkitURL && webkitURL);
29423                         
29424         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29425         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29426         
29427         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29428         this.selectorEl.hide();
29429         
29430         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29431         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29432         
29433         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29434         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29435         this.thumbEl.hide();
29436         
29437         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29438         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29439         
29440         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29441         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29442         this.errorEl.hide();
29443         
29444         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29445         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29446         this.footerEl.hide();
29447         
29448         this.setThumbBoxSize();
29449         
29450         this.bind();
29451         
29452         this.resize();
29453         
29454         this.fireEvent('initial', this);
29455     },
29456
29457     bind : function()
29458     {
29459         var _this = this;
29460         
29461         window.addEventListener("resize", function() { _this.resize(); } );
29462         
29463         this.bodyEl.on('click', this.beforeSelectFile, this);
29464         
29465         if(Roo.isTouch){
29466             this.bodyEl.on('touchstart', this.onTouchStart, this);
29467             this.bodyEl.on('touchmove', this.onTouchMove, this);
29468             this.bodyEl.on('touchend', this.onTouchEnd, this);
29469         }
29470         
29471         if(!Roo.isTouch){
29472             this.bodyEl.on('mousedown', this.onMouseDown, this);
29473             this.bodyEl.on('mousemove', this.onMouseMove, this);
29474             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29475             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29476             Roo.get(document).on('mouseup', this.onMouseUp, this);
29477         }
29478         
29479         this.selectorEl.on('change', this.onFileSelected, this);
29480     },
29481     
29482     reset : function()
29483     {    
29484         this.scale = 0;
29485         this.baseScale = 1;
29486         this.rotate = 0;
29487         this.baseRotate = 1;
29488         this.dragable = false;
29489         this.pinching = false;
29490         this.mouseX = 0;
29491         this.mouseY = 0;
29492         this.cropData = false;
29493         this.notifyEl.dom.innerHTML = this.emptyText;
29494         
29495         this.selectorEl.dom.value = '';
29496         
29497     },
29498     
29499     resize : function()
29500     {
29501         if(this.fireEvent('resize', this) != false){
29502             this.setThumbBoxPosition();
29503             this.setCanvasPosition();
29504         }
29505     },
29506     
29507     onFooterButtonClick : function(e, el, o, type)
29508     {
29509         switch (type) {
29510             case 'rotate-left' :
29511                 this.onRotateLeft(e);
29512                 break;
29513             case 'rotate-right' :
29514                 this.onRotateRight(e);
29515                 break;
29516             case 'picture' :
29517                 this.beforeSelectFile(e);
29518                 break;
29519             case 'trash' :
29520                 this.trash(e);
29521                 break;
29522             case 'crop' :
29523                 this.crop(e);
29524                 break;
29525             case 'download' :
29526                 this.download(e);
29527                 break;
29528             default :
29529                 break;
29530         }
29531         
29532         this.fireEvent('footerbuttonclick', this, type);
29533     },
29534     
29535     beforeSelectFile : function(e)
29536     {
29537         e.preventDefault();
29538         
29539         if(this.fireEvent('beforeselectfile', this) != false){
29540             this.selectorEl.dom.click();
29541         }
29542     },
29543     
29544     onFileSelected : function(e)
29545     {
29546         e.preventDefault();
29547         
29548         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29549             return;
29550         }
29551         
29552         var file = this.selectorEl.dom.files[0];
29553         
29554         if(this.fireEvent('inspect', this, file) != false){
29555             this.prepare(file);
29556         }
29557         
29558     },
29559     
29560     trash : function(e)
29561     {
29562         this.fireEvent('trash', this);
29563     },
29564     
29565     download : function(e)
29566     {
29567         this.fireEvent('download', this);
29568     },
29569     
29570     loadCanvas : function(src)
29571     {   
29572         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29573             
29574             this.reset();
29575             
29576             this.imageEl = document.createElement('img');
29577             
29578             var _this = this;
29579             
29580             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29581             
29582             this.imageEl.src = src;
29583         }
29584     },
29585     
29586     onLoadCanvas : function()
29587     {   
29588         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29589         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29590         
29591         this.bodyEl.un('click', this.beforeSelectFile, this);
29592         
29593         this.notifyEl.hide();
29594         this.thumbEl.show();
29595         this.footerEl.show();
29596         
29597         this.baseRotateLevel();
29598         
29599         if(this.isDocument){
29600             this.setThumbBoxSize();
29601         }
29602         
29603         this.setThumbBoxPosition();
29604         
29605         this.baseScaleLevel();
29606         
29607         this.draw();
29608         
29609         this.resize();
29610         
29611         this.canvasLoaded = true;
29612         
29613         if(this.loadMask){
29614             this.maskEl.unmask();
29615         }
29616         
29617     },
29618     
29619     setCanvasPosition : function()
29620     {   
29621         if(!this.canvasEl){
29622             return;
29623         }
29624         
29625         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29626         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29627         
29628         this.previewEl.setLeft(pw);
29629         this.previewEl.setTop(ph);
29630         
29631     },
29632     
29633     onMouseDown : function(e)
29634     {   
29635         e.stopEvent();
29636         
29637         this.dragable = true;
29638         this.pinching = false;
29639         
29640         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29641             this.dragable = false;
29642             return;
29643         }
29644         
29645         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29646         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29647         
29648     },
29649     
29650     onMouseMove : function(e)
29651     {   
29652         e.stopEvent();
29653         
29654         if(!this.canvasLoaded){
29655             return;
29656         }
29657         
29658         if (!this.dragable){
29659             return;
29660         }
29661         
29662         var minX = Math.ceil(this.thumbEl.getLeft(true));
29663         var minY = Math.ceil(this.thumbEl.getTop(true));
29664         
29665         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29666         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29667         
29668         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29669         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29670         
29671         x = x - this.mouseX;
29672         y = y - this.mouseY;
29673         
29674         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29675         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29676         
29677         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29678         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29679         
29680         this.previewEl.setLeft(bgX);
29681         this.previewEl.setTop(bgY);
29682         
29683         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29684         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29685     },
29686     
29687     onMouseUp : function(e)
29688     {   
29689         e.stopEvent();
29690         
29691         this.dragable = false;
29692     },
29693     
29694     onMouseWheel : function(e)
29695     {   
29696         e.stopEvent();
29697         
29698         this.startScale = this.scale;
29699         
29700         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29701         
29702         if(!this.zoomable()){
29703             this.scale = this.startScale;
29704             return;
29705         }
29706         
29707         this.draw();
29708         
29709         return;
29710     },
29711     
29712     zoomable : function()
29713     {
29714         var minScale = this.thumbEl.getWidth() / this.minWidth;
29715         
29716         if(this.minWidth < this.minHeight){
29717             minScale = this.thumbEl.getHeight() / this.minHeight;
29718         }
29719         
29720         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29721         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29722         
29723         if(
29724                 this.isDocument &&
29725                 (this.rotate == 0 || this.rotate == 180) && 
29726                 (
29727                     width > this.imageEl.OriginWidth || 
29728                     height > this.imageEl.OriginHeight ||
29729                     (width < this.minWidth && height < this.minHeight)
29730                 )
29731         ){
29732             return false;
29733         }
29734         
29735         if(
29736                 this.isDocument &&
29737                 (this.rotate == 90 || this.rotate == 270) && 
29738                 (
29739                     width > this.imageEl.OriginWidth || 
29740                     height > this.imageEl.OriginHeight ||
29741                     (width < this.minHeight && height < this.minWidth)
29742                 )
29743         ){
29744             return false;
29745         }
29746         
29747         if(
29748                 !this.isDocument &&
29749                 (this.rotate == 0 || this.rotate == 180) && 
29750                 (
29751                     width < this.minWidth || 
29752                     width > this.imageEl.OriginWidth || 
29753                     height < this.minHeight || 
29754                     height > this.imageEl.OriginHeight
29755                 )
29756         ){
29757             return false;
29758         }
29759         
29760         if(
29761                 !this.isDocument &&
29762                 (this.rotate == 90 || this.rotate == 270) && 
29763                 (
29764                     width < this.minHeight || 
29765                     width > this.imageEl.OriginWidth || 
29766                     height < this.minWidth || 
29767                     height > this.imageEl.OriginHeight
29768                 )
29769         ){
29770             return false;
29771         }
29772         
29773         return true;
29774         
29775     },
29776     
29777     onRotateLeft : function(e)
29778     {   
29779         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29780             
29781             var minScale = this.thumbEl.getWidth() / this.minWidth;
29782             
29783             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29784             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29785             
29786             this.startScale = this.scale;
29787             
29788             while (this.getScaleLevel() < minScale){
29789             
29790                 this.scale = this.scale + 1;
29791                 
29792                 if(!this.zoomable()){
29793                     break;
29794                 }
29795                 
29796                 if(
29797                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29798                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29799                 ){
29800                     continue;
29801                 }
29802                 
29803                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29804
29805                 this.draw();
29806                 
29807                 return;
29808             }
29809             
29810             this.scale = this.startScale;
29811             
29812             this.onRotateFail();
29813             
29814             return false;
29815         }
29816         
29817         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29818
29819         if(this.isDocument){
29820             this.setThumbBoxSize();
29821             this.setThumbBoxPosition();
29822             this.setCanvasPosition();
29823         }
29824         
29825         this.draw();
29826         
29827         this.fireEvent('rotate', this, 'left');
29828         
29829     },
29830     
29831     onRotateRight : function(e)
29832     {
29833         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29834             
29835             var minScale = this.thumbEl.getWidth() / this.minWidth;
29836         
29837             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29838             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29839             
29840             this.startScale = this.scale;
29841             
29842             while (this.getScaleLevel() < minScale){
29843             
29844                 this.scale = this.scale + 1;
29845                 
29846                 if(!this.zoomable()){
29847                     break;
29848                 }
29849                 
29850                 if(
29851                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29852                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29853                 ){
29854                     continue;
29855                 }
29856                 
29857                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29858
29859                 this.draw();
29860                 
29861                 return;
29862             }
29863             
29864             this.scale = this.startScale;
29865             
29866             this.onRotateFail();
29867             
29868             return false;
29869         }
29870         
29871         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29872
29873         if(this.isDocument){
29874             this.setThumbBoxSize();
29875             this.setThumbBoxPosition();
29876             this.setCanvasPosition();
29877         }
29878         
29879         this.draw();
29880         
29881         this.fireEvent('rotate', this, 'right');
29882     },
29883     
29884     onRotateFail : function()
29885     {
29886         this.errorEl.show(true);
29887         
29888         var _this = this;
29889         
29890         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29891     },
29892     
29893     draw : function()
29894     {
29895         this.previewEl.dom.innerHTML = '';
29896         
29897         var canvasEl = document.createElement("canvas");
29898         
29899         var contextEl = canvasEl.getContext("2d");
29900         
29901         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29902         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29903         var center = this.imageEl.OriginWidth / 2;
29904         
29905         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29906             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29907             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29908             center = this.imageEl.OriginHeight / 2;
29909         }
29910         
29911         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29912         
29913         contextEl.translate(center, center);
29914         contextEl.rotate(this.rotate * Math.PI / 180);
29915
29916         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29917         
29918         this.canvasEl = document.createElement("canvas");
29919         
29920         this.contextEl = this.canvasEl.getContext("2d");
29921         
29922         switch (this.rotate) {
29923             case 0 :
29924                 
29925                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29926                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29927                 
29928                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29929                 
29930                 break;
29931             case 90 : 
29932                 
29933                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29934                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29935                 
29936                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29937                     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);
29938                     break;
29939                 }
29940                 
29941                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29942                 
29943                 break;
29944             case 180 :
29945                 
29946                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29947                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29948                 
29949                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29950                     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);
29951                     break;
29952                 }
29953                 
29954                 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);
29955                 
29956                 break;
29957             case 270 :
29958                 
29959                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29960                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29961         
29962                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29963                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29964                     break;
29965                 }
29966                 
29967                 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);
29968                 
29969                 break;
29970             default : 
29971                 break;
29972         }
29973         
29974         this.previewEl.appendChild(this.canvasEl);
29975         
29976         this.setCanvasPosition();
29977     },
29978     
29979     crop : function()
29980     {
29981         if(!this.canvasLoaded){
29982             return;
29983         }
29984         
29985         var imageCanvas = document.createElement("canvas");
29986         
29987         var imageContext = imageCanvas.getContext("2d");
29988         
29989         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29990         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29991         
29992         var center = imageCanvas.width / 2;
29993         
29994         imageContext.translate(center, center);
29995         
29996         imageContext.rotate(this.rotate * Math.PI / 180);
29997         
29998         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29999         
30000         var canvas = document.createElement("canvas");
30001         
30002         var context = canvas.getContext("2d");
30003                 
30004         canvas.width = this.minWidth;
30005         canvas.height = this.minHeight;
30006
30007         switch (this.rotate) {
30008             case 0 :
30009                 
30010                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30011                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30012                 
30013                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30014                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30015                 
30016                 var targetWidth = this.minWidth - 2 * x;
30017                 var targetHeight = this.minHeight - 2 * y;
30018                 
30019                 var scale = 1;
30020                 
30021                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30022                     scale = targetWidth / width;
30023                 }
30024                 
30025                 if(x > 0 && y == 0){
30026                     scale = targetHeight / height;
30027                 }
30028                 
30029                 if(x > 0 && y > 0){
30030                     scale = targetWidth / width;
30031                     
30032                     if(width < height){
30033                         scale = targetHeight / height;
30034                     }
30035                 }
30036                 
30037                 context.scale(scale, scale);
30038                 
30039                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30040                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30041
30042                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30043                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30044
30045                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30046                 
30047                 break;
30048             case 90 : 
30049                 
30050                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30051                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30052                 
30053                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30054                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30055                 
30056                 var targetWidth = this.minWidth - 2 * x;
30057                 var targetHeight = this.minHeight - 2 * y;
30058                 
30059                 var scale = 1;
30060                 
30061                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30062                     scale = targetWidth / width;
30063                 }
30064                 
30065                 if(x > 0 && y == 0){
30066                     scale = targetHeight / height;
30067                 }
30068                 
30069                 if(x > 0 && y > 0){
30070                     scale = targetWidth / width;
30071                     
30072                     if(width < height){
30073                         scale = targetHeight / height;
30074                     }
30075                 }
30076                 
30077                 context.scale(scale, scale);
30078                 
30079                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30080                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30081
30082                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30083                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30084                 
30085                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30086                 
30087                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30088                 
30089                 break;
30090             case 180 :
30091                 
30092                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30093                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30094                 
30095                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30096                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30097                 
30098                 var targetWidth = this.minWidth - 2 * x;
30099                 var targetHeight = this.minHeight - 2 * y;
30100                 
30101                 var scale = 1;
30102                 
30103                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30104                     scale = targetWidth / width;
30105                 }
30106                 
30107                 if(x > 0 && y == 0){
30108                     scale = targetHeight / height;
30109                 }
30110                 
30111                 if(x > 0 && y > 0){
30112                     scale = targetWidth / width;
30113                     
30114                     if(width < height){
30115                         scale = targetHeight / height;
30116                     }
30117                 }
30118                 
30119                 context.scale(scale, scale);
30120                 
30121                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30122                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30123
30124                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30125                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30126
30127                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30128                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30129                 
30130                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30131                 
30132                 break;
30133             case 270 :
30134                 
30135                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30136                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30137                 
30138                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30139                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30140                 
30141                 var targetWidth = this.minWidth - 2 * x;
30142                 var targetHeight = this.minHeight - 2 * y;
30143                 
30144                 var scale = 1;
30145                 
30146                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30147                     scale = targetWidth / width;
30148                 }
30149                 
30150                 if(x > 0 && y == 0){
30151                     scale = targetHeight / height;
30152                 }
30153                 
30154                 if(x > 0 && y > 0){
30155                     scale = targetWidth / width;
30156                     
30157                     if(width < height){
30158                         scale = targetHeight / height;
30159                     }
30160                 }
30161                 
30162                 context.scale(scale, scale);
30163                 
30164                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30165                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30166
30167                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30168                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30169                 
30170                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30171                 
30172                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30173                 
30174                 break;
30175             default : 
30176                 break;
30177         }
30178         
30179         this.cropData = canvas.toDataURL(this.cropType);
30180         
30181         if(this.fireEvent('crop', this, this.cropData) !== false){
30182             this.process(this.file, this.cropData);
30183         }
30184         
30185         return;
30186         
30187     },
30188     
30189     setThumbBoxSize : function()
30190     {
30191         var width, height;
30192         
30193         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30194             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30195             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30196             
30197             this.minWidth = width;
30198             this.minHeight = height;
30199             
30200             if(this.rotate == 90 || this.rotate == 270){
30201                 this.minWidth = height;
30202                 this.minHeight = width;
30203             }
30204         }
30205         
30206         height = 300;
30207         width = Math.ceil(this.minWidth * height / this.minHeight);
30208         
30209         if(this.minWidth > this.minHeight){
30210             width = 300;
30211             height = Math.ceil(this.minHeight * width / this.minWidth);
30212         }
30213         
30214         this.thumbEl.setStyle({
30215             width : width + 'px',
30216             height : height + 'px'
30217         });
30218
30219         return;
30220             
30221     },
30222     
30223     setThumbBoxPosition : function()
30224     {
30225         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30226         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30227         
30228         this.thumbEl.setLeft(x);
30229         this.thumbEl.setTop(y);
30230         
30231     },
30232     
30233     baseRotateLevel : function()
30234     {
30235         this.baseRotate = 1;
30236         
30237         if(
30238                 typeof(this.exif) != 'undefined' &&
30239                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30240                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30241         ){
30242             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30243         }
30244         
30245         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30246         
30247     },
30248     
30249     baseScaleLevel : function()
30250     {
30251         var width, height;
30252         
30253         if(this.isDocument){
30254             
30255             if(this.baseRotate == 6 || this.baseRotate == 8){
30256             
30257                 height = this.thumbEl.getHeight();
30258                 this.baseScale = height / this.imageEl.OriginWidth;
30259
30260                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30261                     width = this.thumbEl.getWidth();
30262                     this.baseScale = width / this.imageEl.OriginHeight;
30263                 }
30264
30265                 return;
30266             }
30267
30268             height = this.thumbEl.getHeight();
30269             this.baseScale = height / this.imageEl.OriginHeight;
30270
30271             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30272                 width = this.thumbEl.getWidth();
30273                 this.baseScale = width / this.imageEl.OriginWidth;
30274             }
30275
30276             return;
30277         }
30278         
30279         if(this.baseRotate == 6 || this.baseRotate == 8){
30280             
30281             width = this.thumbEl.getHeight();
30282             this.baseScale = width / this.imageEl.OriginHeight;
30283             
30284             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30285                 height = this.thumbEl.getWidth();
30286                 this.baseScale = height / this.imageEl.OriginHeight;
30287             }
30288             
30289             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30290                 height = this.thumbEl.getWidth();
30291                 this.baseScale = height / this.imageEl.OriginHeight;
30292                 
30293                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30294                     width = this.thumbEl.getHeight();
30295                     this.baseScale = width / this.imageEl.OriginWidth;
30296                 }
30297             }
30298             
30299             return;
30300         }
30301         
30302         width = this.thumbEl.getWidth();
30303         this.baseScale = width / this.imageEl.OriginWidth;
30304         
30305         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30306             height = this.thumbEl.getHeight();
30307             this.baseScale = height / this.imageEl.OriginHeight;
30308         }
30309         
30310         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30311             
30312             height = this.thumbEl.getHeight();
30313             this.baseScale = height / this.imageEl.OriginHeight;
30314             
30315             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30316                 width = this.thumbEl.getWidth();
30317                 this.baseScale = width / this.imageEl.OriginWidth;
30318             }
30319             
30320         }
30321         
30322         return;
30323     },
30324     
30325     getScaleLevel : function()
30326     {
30327         return this.baseScale * Math.pow(1.1, this.scale);
30328     },
30329     
30330     onTouchStart : function(e)
30331     {
30332         if(!this.canvasLoaded){
30333             this.beforeSelectFile(e);
30334             return;
30335         }
30336         
30337         var touches = e.browserEvent.touches;
30338         
30339         if(!touches){
30340             return;
30341         }
30342         
30343         if(touches.length == 1){
30344             this.onMouseDown(e);
30345             return;
30346         }
30347         
30348         if(touches.length != 2){
30349             return;
30350         }
30351         
30352         var coords = [];
30353         
30354         for(var i = 0, finger; finger = touches[i]; i++){
30355             coords.push(finger.pageX, finger.pageY);
30356         }
30357         
30358         var x = Math.pow(coords[0] - coords[2], 2);
30359         var y = Math.pow(coords[1] - coords[3], 2);
30360         
30361         this.startDistance = Math.sqrt(x + y);
30362         
30363         this.startScale = this.scale;
30364         
30365         this.pinching = true;
30366         this.dragable = false;
30367         
30368     },
30369     
30370     onTouchMove : function(e)
30371     {
30372         if(!this.pinching && !this.dragable){
30373             return;
30374         }
30375         
30376         var touches = e.browserEvent.touches;
30377         
30378         if(!touches){
30379             return;
30380         }
30381         
30382         if(this.dragable){
30383             this.onMouseMove(e);
30384             return;
30385         }
30386         
30387         var coords = [];
30388         
30389         for(var i = 0, finger; finger = touches[i]; i++){
30390             coords.push(finger.pageX, finger.pageY);
30391         }
30392         
30393         var x = Math.pow(coords[0] - coords[2], 2);
30394         var y = Math.pow(coords[1] - coords[3], 2);
30395         
30396         this.endDistance = Math.sqrt(x + y);
30397         
30398         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30399         
30400         if(!this.zoomable()){
30401             this.scale = this.startScale;
30402             return;
30403         }
30404         
30405         this.draw();
30406         
30407     },
30408     
30409     onTouchEnd : function(e)
30410     {
30411         this.pinching = false;
30412         this.dragable = false;
30413         
30414     },
30415     
30416     process : function(file, crop)
30417     {
30418         if(this.loadMask){
30419             this.maskEl.mask(this.loadingText);
30420         }
30421         
30422         this.xhr = new XMLHttpRequest();
30423         
30424         file.xhr = this.xhr;
30425
30426         this.xhr.open(this.method, this.url, true);
30427         
30428         var headers = {
30429             "Accept": "application/json",
30430             "Cache-Control": "no-cache",
30431             "X-Requested-With": "XMLHttpRequest"
30432         };
30433         
30434         for (var headerName in headers) {
30435             var headerValue = headers[headerName];
30436             if (headerValue) {
30437                 this.xhr.setRequestHeader(headerName, headerValue);
30438             }
30439         }
30440         
30441         var _this = this;
30442         
30443         this.xhr.onload = function()
30444         {
30445             _this.xhrOnLoad(_this.xhr);
30446         }
30447         
30448         this.xhr.onerror = function()
30449         {
30450             _this.xhrOnError(_this.xhr);
30451         }
30452         
30453         var formData = new FormData();
30454
30455         formData.append('returnHTML', 'NO');
30456         
30457         if(crop){
30458             formData.append('crop', crop);
30459         }
30460         
30461         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30462             formData.append(this.paramName, file, file.name);
30463         }
30464         
30465         if(typeof(file.filename) != 'undefined'){
30466             formData.append('filename', file.filename);
30467         }
30468         
30469         if(typeof(file.mimetype) != 'undefined'){
30470             formData.append('mimetype', file.mimetype);
30471         }
30472         
30473         if(this.fireEvent('arrange', this, formData) != false){
30474             this.xhr.send(formData);
30475         };
30476     },
30477     
30478     xhrOnLoad : function(xhr)
30479     {
30480         if(this.loadMask){
30481             this.maskEl.unmask();
30482         }
30483         
30484         if (xhr.readyState !== 4) {
30485             this.fireEvent('exception', this, xhr);
30486             return;
30487         }
30488
30489         var response = Roo.decode(xhr.responseText);
30490         
30491         if(!response.success){
30492             this.fireEvent('exception', this, xhr);
30493             return;
30494         }
30495         
30496         var response = Roo.decode(xhr.responseText);
30497         
30498         this.fireEvent('upload', this, response);
30499         
30500     },
30501     
30502     xhrOnError : function()
30503     {
30504         if(this.loadMask){
30505             this.maskEl.unmask();
30506         }
30507         
30508         Roo.log('xhr on error');
30509         
30510         var response = Roo.decode(xhr.responseText);
30511           
30512         Roo.log(response);
30513         
30514     },
30515     
30516     prepare : function(file)
30517     {   
30518         if(this.loadMask){
30519             this.maskEl.mask(this.loadingText);
30520         }
30521         
30522         this.file = false;
30523         this.exif = {};
30524         
30525         if(typeof(file) === 'string'){
30526             this.loadCanvas(file);
30527             return;
30528         }
30529         
30530         if(!file || !this.urlAPI){
30531             return;
30532         }
30533         
30534         this.file = file;
30535         this.cropType = file.type;
30536         
30537         var _this = this;
30538         
30539         if(this.fireEvent('prepare', this, this.file) != false){
30540             
30541             var reader = new FileReader();
30542             
30543             reader.onload = function (e) {
30544                 if (e.target.error) {
30545                     Roo.log(e.target.error);
30546                     return;
30547                 }
30548                 
30549                 var buffer = e.target.result,
30550                     dataView = new DataView(buffer),
30551                     offset = 2,
30552                     maxOffset = dataView.byteLength - 4,
30553                     markerBytes,
30554                     markerLength;
30555                 
30556                 if (dataView.getUint16(0) === 0xffd8) {
30557                     while (offset < maxOffset) {
30558                         markerBytes = dataView.getUint16(offset);
30559                         
30560                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30561                             markerLength = dataView.getUint16(offset + 2) + 2;
30562                             if (offset + markerLength > dataView.byteLength) {
30563                                 Roo.log('Invalid meta data: Invalid segment size.');
30564                                 break;
30565                             }
30566                             
30567                             if(markerBytes == 0xffe1){
30568                                 _this.parseExifData(
30569                                     dataView,
30570                                     offset,
30571                                     markerLength
30572                                 );
30573                             }
30574                             
30575                             offset += markerLength;
30576                             
30577                             continue;
30578                         }
30579                         
30580                         break;
30581                     }
30582                     
30583                 }
30584                 
30585                 var url = _this.urlAPI.createObjectURL(_this.file);
30586                 
30587                 _this.loadCanvas(url);
30588                 
30589                 return;
30590             }
30591             
30592             reader.readAsArrayBuffer(this.file);
30593             
30594         }
30595         
30596     },
30597     
30598     parseExifData : function(dataView, offset, length)
30599     {
30600         var tiffOffset = offset + 10,
30601             littleEndian,
30602             dirOffset;
30603     
30604         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30605             // No Exif data, might be XMP data instead
30606             return;
30607         }
30608         
30609         // Check for the ASCII code for "Exif" (0x45786966):
30610         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30611             // No Exif data, might be XMP data instead
30612             return;
30613         }
30614         if (tiffOffset + 8 > dataView.byteLength) {
30615             Roo.log('Invalid Exif data: Invalid segment size.');
30616             return;
30617         }
30618         // Check for the two null bytes:
30619         if (dataView.getUint16(offset + 8) !== 0x0000) {
30620             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30621             return;
30622         }
30623         // Check the byte alignment:
30624         switch (dataView.getUint16(tiffOffset)) {
30625         case 0x4949:
30626             littleEndian = true;
30627             break;
30628         case 0x4D4D:
30629             littleEndian = false;
30630             break;
30631         default:
30632             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30633             return;
30634         }
30635         // Check for the TIFF tag marker (0x002A):
30636         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30637             Roo.log('Invalid Exif data: Missing TIFF marker.');
30638             return;
30639         }
30640         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30641         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30642         
30643         this.parseExifTags(
30644             dataView,
30645             tiffOffset,
30646             tiffOffset + dirOffset,
30647             littleEndian
30648         );
30649     },
30650     
30651     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30652     {
30653         var tagsNumber,
30654             dirEndOffset,
30655             i;
30656         if (dirOffset + 6 > dataView.byteLength) {
30657             Roo.log('Invalid Exif data: Invalid directory offset.');
30658             return;
30659         }
30660         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30661         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30662         if (dirEndOffset + 4 > dataView.byteLength) {
30663             Roo.log('Invalid Exif data: Invalid directory size.');
30664             return;
30665         }
30666         for (i = 0; i < tagsNumber; i += 1) {
30667             this.parseExifTag(
30668                 dataView,
30669                 tiffOffset,
30670                 dirOffset + 2 + 12 * i, // tag offset
30671                 littleEndian
30672             );
30673         }
30674         // Return the offset to the next directory:
30675         return dataView.getUint32(dirEndOffset, littleEndian);
30676     },
30677     
30678     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30679     {
30680         var tag = dataView.getUint16(offset, littleEndian);
30681         
30682         this.exif[tag] = this.getExifValue(
30683             dataView,
30684             tiffOffset,
30685             offset,
30686             dataView.getUint16(offset + 2, littleEndian), // tag type
30687             dataView.getUint32(offset + 4, littleEndian), // tag length
30688             littleEndian
30689         );
30690     },
30691     
30692     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30693     {
30694         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30695             tagSize,
30696             dataOffset,
30697             values,
30698             i,
30699             str,
30700             c;
30701     
30702         if (!tagType) {
30703             Roo.log('Invalid Exif data: Invalid tag type.');
30704             return;
30705         }
30706         
30707         tagSize = tagType.size * length;
30708         // Determine if the value is contained in the dataOffset bytes,
30709         // or if the value at the dataOffset is a pointer to the actual data:
30710         dataOffset = tagSize > 4 ?
30711                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30712         if (dataOffset + tagSize > dataView.byteLength) {
30713             Roo.log('Invalid Exif data: Invalid data offset.');
30714             return;
30715         }
30716         if (length === 1) {
30717             return tagType.getValue(dataView, dataOffset, littleEndian);
30718         }
30719         values = [];
30720         for (i = 0; i < length; i += 1) {
30721             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30722         }
30723         
30724         if (tagType.ascii) {
30725             str = '';
30726             // Concatenate the chars:
30727             for (i = 0; i < values.length; i += 1) {
30728                 c = values[i];
30729                 // Ignore the terminating NULL byte(s):
30730                 if (c === '\u0000') {
30731                     break;
30732                 }
30733                 str += c;
30734             }
30735             return str;
30736         }
30737         return values;
30738     }
30739     
30740 });
30741
30742 Roo.apply(Roo.bootstrap.UploadCropbox, {
30743     tags : {
30744         'Orientation': 0x0112
30745     },
30746     
30747     Orientation: {
30748             1: 0, //'top-left',
30749 //            2: 'top-right',
30750             3: 180, //'bottom-right',
30751 //            4: 'bottom-left',
30752 //            5: 'left-top',
30753             6: 90, //'right-top',
30754 //            7: 'right-bottom',
30755             8: 270 //'left-bottom'
30756     },
30757     
30758     exifTagTypes : {
30759         // byte, 8-bit unsigned int:
30760         1: {
30761             getValue: function (dataView, dataOffset) {
30762                 return dataView.getUint8(dataOffset);
30763             },
30764             size: 1
30765         },
30766         // ascii, 8-bit byte:
30767         2: {
30768             getValue: function (dataView, dataOffset) {
30769                 return String.fromCharCode(dataView.getUint8(dataOffset));
30770             },
30771             size: 1,
30772             ascii: true
30773         },
30774         // short, 16 bit int:
30775         3: {
30776             getValue: function (dataView, dataOffset, littleEndian) {
30777                 return dataView.getUint16(dataOffset, littleEndian);
30778             },
30779             size: 2
30780         },
30781         // long, 32 bit int:
30782         4: {
30783             getValue: function (dataView, dataOffset, littleEndian) {
30784                 return dataView.getUint32(dataOffset, littleEndian);
30785             },
30786             size: 4
30787         },
30788         // rational = two long values, first is numerator, second is denominator:
30789         5: {
30790             getValue: function (dataView, dataOffset, littleEndian) {
30791                 return dataView.getUint32(dataOffset, littleEndian) /
30792                     dataView.getUint32(dataOffset + 4, littleEndian);
30793             },
30794             size: 8
30795         },
30796         // slong, 32 bit signed int:
30797         9: {
30798             getValue: function (dataView, dataOffset, littleEndian) {
30799                 return dataView.getInt32(dataOffset, littleEndian);
30800             },
30801             size: 4
30802         },
30803         // srational, two slongs, first is numerator, second is denominator:
30804         10: {
30805             getValue: function (dataView, dataOffset, littleEndian) {
30806                 return dataView.getInt32(dataOffset, littleEndian) /
30807                     dataView.getInt32(dataOffset + 4, littleEndian);
30808             },
30809             size: 8
30810         }
30811     },
30812     
30813     footer : {
30814         STANDARD : [
30815             {
30816                 tag : 'div',
30817                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30818                 action : 'rotate-left',
30819                 cn : [
30820                     {
30821                         tag : 'button',
30822                         cls : 'btn btn-default',
30823                         html : '<i class="fa fa-undo"></i>'
30824                     }
30825                 ]
30826             },
30827             {
30828                 tag : 'div',
30829                 cls : 'btn-group roo-upload-cropbox-picture',
30830                 action : 'picture',
30831                 cn : [
30832                     {
30833                         tag : 'button',
30834                         cls : 'btn btn-default',
30835                         html : '<i class="fa fa-picture-o"></i>'
30836                     }
30837                 ]
30838             },
30839             {
30840                 tag : 'div',
30841                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30842                 action : 'rotate-right',
30843                 cn : [
30844                     {
30845                         tag : 'button',
30846                         cls : 'btn btn-default',
30847                         html : '<i class="fa fa-repeat"></i>'
30848                     }
30849                 ]
30850             }
30851         ],
30852         DOCUMENT : [
30853             {
30854                 tag : 'div',
30855                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30856                 action : 'rotate-left',
30857                 cn : [
30858                     {
30859                         tag : 'button',
30860                         cls : 'btn btn-default',
30861                         html : '<i class="fa fa-undo"></i>'
30862                     }
30863                 ]
30864             },
30865             {
30866                 tag : 'div',
30867                 cls : 'btn-group roo-upload-cropbox-download',
30868                 action : 'download',
30869                 cn : [
30870                     {
30871                         tag : 'button',
30872                         cls : 'btn btn-default',
30873                         html : '<i class="fa fa-download"></i>'
30874                     }
30875                 ]
30876             },
30877             {
30878                 tag : 'div',
30879                 cls : 'btn-group roo-upload-cropbox-crop',
30880                 action : 'crop',
30881                 cn : [
30882                     {
30883                         tag : 'button',
30884                         cls : 'btn btn-default',
30885                         html : '<i class="fa fa-crop"></i>'
30886                     }
30887                 ]
30888             },
30889             {
30890                 tag : 'div',
30891                 cls : 'btn-group roo-upload-cropbox-trash',
30892                 action : 'trash',
30893                 cn : [
30894                     {
30895                         tag : 'button',
30896                         cls : 'btn btn-default',
30897                         html : '<i class="fa fa-trash"></i>'
30898                     }
30899                 ]
30900             },
30901             {
30902                 tag : 'div',
30903                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30904                 action : 'rotate-right',
30905                 cn : [
30906                     {
30907                         tag : 'button',
30908                         cls : 'btn btn-default',
30909                         html : '<i class="fa fa-repeat"></i>'
30910                     }
30911                 ]
30912             }
30913         ],
30914         ROTATOR : [
30915             {
30916                 tag : 'div',
30917                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30918                 action : 'rotate-left',
30919                 cn : [
30920                     {
30921                         tag : 'button',
30922                         cls : 'btn btn-default',
30923                         html : '<i class="fa fa-undo"></i>'
30924                     }
30925                 ]
30926             },
30927             {
30928                 tag : 'div',
30929                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30930                 action : 'rotate-right',
30931                 cn : [
30932                     {
30933                         tag : 'button',
30934                         cls : 'btn btn-default',
30935                         html : '<i class="fa fa-repeat"></i>'
30936                     }
30937                 ]
30938             }
30939         ]
30940     }
30941 });
30942
30943 /*
30944 * Licence: LGPL
30945 */
30946
30947 /**
30948  * @class Roo.bootstrap.DocumentManager
30949  * @extends Roo.bootstrap.Component
30950  * Bootstrap DocumentManager class
30951  * @cfg {String} paramName default 'imageUpload'
30952  * @cfg {String} toolTipName default 'filename'
30953  * @cfg {String} method default POST
30954  * @cfg {String} url action url
30955  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30956  * @cfg {Boolean} multiple multiple upload default true
30957  * @cfg {Number} thumbSize default 300
30958  * @cfg {String} fieldLabel
30959  * @cfg {Number} labelWidth default 4
30960  * @cfg {String} labelAlign (left|top) default left
30961  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30962 * @cfg {Number} labellg set the width of label (1-12)
30963  * @cfg {Number} labelmd set the width of label (1-12)
30964  * @cfg {Number} labelsm set the width of label (1-12)
30965  * @cfg {Number} labelxs set the width of label (1-12)
30966  * 
30967  * @constructor
30968  * Create a new DocumentManager
30969  * @param {Object} config The config object
30970  */
30971
30972 Roo.bootstrap.DocumentManager = function(config){
30973     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30974     
30975     this.files = [];
30976     this.delegates = [];
30977     
30978     this.addEvents({
30979         /**
30980          * @event initial
30981          * Fire when initial the DocumentManager
30982          * @param {Roo.bootstrap.DocumentManager} this
30983          */
30984         "initial" : true,
30985         /**
30986          * @event inspect
30987          * inspect selected file
30988          * @param {Roo.bootstrap.DocumentManager} this
30989          * @param {File} file
30990          */
30991         "inspect" : true,
30992         /**
30993          * @event exception
30994          * Fire when xhr load exception
30995          * @param {Roo.bootstrap.DocumentManager} this
30996          * @param {XMLHttpRequest} xhr
30997          */
30998         "exception" : true,
30999         /**
31000          * @event afterupload
31001          * Fire when xhr load exception
31002          * @param {Roo.bootstrap.DocumentManager} this
31003          * @param {XMLHttpRequest} xhr
31004          */
31005         "afterupload" : true,
31006         /**
31007          * @event prepare
31008          * prepare the form data
31009          * @param {Roo.bootstrap.DocumentManager} this
31010          * @param {Object} formData
31011          */
31012         "prepare" : true,
31013         /**
31014          * @event remove
31015          * Fire when remove the file
31016          * @param {Roo.bootstrap.DocumentManager} this
31017          * @param {Object} file
31018          */
31019         "remove" : true,
31020         /**
31021          * @event refresh
31022          * Fire after refresh the file
31023          * @param {Roo.bootstrap.DocumentManager} this
31024          */
31025         "refresh" : true,
31026         /**
31027          * @event click
31028          * Fire after click the image
31029          * @param {Roo.bootstrap.DocumentManager} this
31030          * @param {Object} file
31031          */
31032         "click" : true,
31033         /**
31034          * @event edit
31035          * Fire when upload a image and editable set to true
31036          * @param {Roo.bootstrap.DocumentManager} this
31037          * @param {Object} file
31038          */
31039         "edit" : true,
31040         /**
31041          * @event beforeselectfile
31042          * Fire before select file
31043          * @param {Roo.bootstrap.DocumentManager} this
31044          */
31045         "beforeselectfile" : true,
31046         /**
31047          * @event process
31048          * Fire before process file
31049          * @param {Roo.bootstrap.DocumentManager} this
31050          * @param {Object} file
31051          */
31052         "process" : true,
31053         /**
31054          * @event previewrendered
31055          * Fire when preview rendered
31056          * @param {Roo.bootstrap.DocumentManager} this
31057          * @param {Object} file
31058          */
31059         "previewrendered" : true,
31060         /**
31061          */
31062         "previewResize" : true
31063         
31064     });
31065 };
31066
31067 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31068     
31069     boxes : 0,
31070     inputName : '',
31071     thumbSize : 300,
31072     multiple : true,
31073     files : false,
31074     method : 'POST',
31075     url : '',
31076     paramName : 'imageUpload',
31077     toolTipName : 'filename',
31078     fieldLabel : '',
31079     labelWidth : 4,
31080     labelAlign : 'left',
31081     editable : true,
31082     delegates : false,
31083     xhr : false, 
31084     
31085     labellg : 0,
31086     labelmd : 0,
31087     labelsm : 0,
31088     labelxs : 0,
31089     
31090     getAutoCreate : function()
31091     {   
31092         var managerWidget = {
31093             tag : 'div',
31094             cls : 'roo-document-manager',
31095             cn : [
31096                 {
31097                     tag : 'input',
31098                     cls : 'roo-document-manager-selector',
31099                     type : 'file'
31100                 },
31101                 {
31102                     tag : 'div',
31103                     cls : 'roo-document-manager-uploader',
31104                     cn : [
31105                         {
31106                             tag : 'div',
31107                             cls : 'roo-document-manager-upload-btn',
31108                             html : '<i class="fa fa-plus"></i>'
31109                         }
31110                     ]
31111                     
31112                 }
31113             ]
31114         };
31115         
31116         var content = [
31117             {
31118                 tag : 'div',
31119                 cls : 'column col-md-12',
31120                 cn : managerWidget
31121             }
31122         ];
31123         
31124         if(this.fieldLabel.length){
31125             
31126             content = [
31127                 {
31128                     tag : 'div',
31129                     cls : 'column col-md-12',
31130                     html : this.fieldLabel
31131                 },
31132                 {
31133                     tag : 'div',
31134                     cls : 'column col-md-12',
31135                     cn : managerWidget
31136                 }
31137             ];
31138
31139             if(this.labelAlign == 'left'){
31140                 content = [
31141                     {
31142                         tag : 'div',
31143                         cls : 'column',
31144                         html : this.fieldLabel
31145                     },
31146                     {
31147                         tag : 'div',
31148                         cls : 'column',
31149                         cn : managerWidget
31150                     }
31151                 ];
31152                 
31153                 if(this.labelWidth > 12){
31154                     content[0].style = "width: " + this.labelWidth + 'px';
31155                 }
31156
31157                 if(this.labelWidth < 13 && this.labelmd == 0){
31158                     this.labelmd = this.labelWidth;
31159                 }
31160
31161                 if(this.labellg > 0){
31162                     content[0].cls += ' col-lg-' + this.labellg;
31163                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31164                 }
31165
31166                 if(this.labelmd > 0){
31167                     content[0].cls += ' col-md-' + this.labelmd;
31168                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31169                 }
31170
31171                 if(this.labelsm > 0){
31172                     content[0].cls += ' col-sm-' + this.labelsm;
31173                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31174                 }
31175
31176                 if(this.labelxs > 0){
31177                     content[0].cls += ' col-xs-' + this.labelxs;
31178                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31179                 }
31180                 
31181             }
31182         }
31183         
31184         var cfg = {
31185             tag : 'div',
31186             cls : 'row clearfix',
31187             cn : content
31188         };
31189         
31190         return cfg;
31191         
31192     },
31193     
31194     initEvents : function()
31195     {
31196         this.managerEl = this.el.select('.roo-document-manager', true).first();
31197         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31198         
31199         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31200         this.selectorEl.hide();
31201         
31202         if(this.multiple){
31203             this.selectorEl.attr('multiple', 'multiple');
31204         }
31205         
31206         this.selectorEl.on('change', this.onFileSelected, this);
31207         
31208         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31209         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31210         
31211         this.uploader.on('click', this.onUploaderClick, this);
31212         
31213         this.renderProgressDialog();
31214         
31215         var _this = this;
31216         
31217         window.addEventListener("resize", function() { _this.refresh(); } );
31218         
31219         this.fireEvent('initial', this);
31220     },
31221     
31222     renderProgressDialog : function()
31223     {
31224         var _this = this;
31225         
31226         this.progressDialog = new Roo.bootstrap.Modal({
31227             cls : 'roo-document-manager-progress-dialog',
31228             allow_close : false,
31229             animate : false,
31230             title : '',
31231             buttons : [
31232                 {
31233                     name  :'cancel',
31234                     weight : 'danger',
31235                     html : 'Cancel'
31236                 }
31237             ], 
31238             listeners : { 
31239                 btnclick : function() {
31240                     _this.uploadCancel();
31241                     this.hide();
31242                 }
31243             }
31244         });
31245          
31246         this.progressDialog.render(Roo.get(document.body));
31247          
31248         this.progress = new Roo.bootstrap.Progress({
31249             cls : 'roo-document-manager-progress',
31250             active : true,
31251             striped : true
31252         });
31253         
31254         this.progress.render(this.progressDialog.getChildContainer());
31255         
31256         this.progressBar = new Roo.bootstrap.ProgressBar({
31257             cls : 'roo-document-manager-progress-bar',
31258             aria_valuenow : 0,
31259             aria_valuemin : 0,
31260             aria_valuemax : 12,
31261             panel : 'success'
31262         });
31263         
31264         this.progressBar.render(this.progress.getChildContainer());
31265     },
31266     
31267     onUploaderClick : function(e)
31268     {
31269         e.preventDefault();
31270      
31271         if(this.fireEvent('beforeselectfile', this) != false){
31272             this.selectorEl.dom.click();
31273         }
31274         
31275     },
31276     
31277     onFileSelected : function(e)
31278     {
31279         e.preventDefault();
31280         
31281         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31282             return;
31283         }
31284         
31285         Roo.each(this.selectorEl.dom.files, function(file){
31286             if(this.fireEvent('inspect', this, file) != false){
31287                 this.files.push(file);
31288             }
31289         }, this);
31290         
31291         this.queue();
31292         
31293     },
31294     
31295     queue : function()
31296     {
31297         this.selectorEl.dom.value = '';
31298         
31299         if(!this.files || !this.files.length){
31300             return;
31301         }
31302         
31303         if(this.boxes > 0 && this.files.length > this.boxes){
31304             this.files = this.files.slice(0, this.boxes);
31305         }
31306         
31307         this.uploader.show();
31308         
31309         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31310             this.uploader.hide();
31311         }
31312         
31313         var _this = this;
31314         
31315         var files = [];
31316         
31317         var docs = [];
31318         
31319         Roo.each(this.files, function(file){
31320             
31321             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31322                 var f = this.renderPreview(file);
31323                 files.push(f);
31324                 return;
31325             }
31326             
31327             if(file.type.indexOf('image') != -1){
31328                 this.delegates.push(
31329                     (function(){
31330                         _this.process(file);
31331                     }).createDelegate(this)
31332                 );
31333         
31334                 return;
31335             }
31336             
31337             docs.push(
31338                 (function(){
31339                     _this.process(file);
31340                 }).createDelegate(this)
31341             );
31342             
31343         }, this);
31344         
31345         this.files = files;
31346         
31347         this.delegates = this.delegates.concat(docs);
31348         
31349         if(!this.delegates.length){
31350             this.refresh();
31351             return;
31352         }
31353         
31354         this.progressBar.aria_valuemax = this.delegates.length;
31355         
31356         this.arrange();
31357         
31358         return;
31359     },
31360     
31361     arrange : function()
31362     {
31363         if(!this.delegates.length){
31364             this.progressDialog.hide();
31365             this.refresh();
31366             return;
31367         }
31368         
31369         var delegate = this.delegates.shift();
31370         
31371         this.progressDialog.show();
31372         
31373         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31374         
31375         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31376         
31377         delegate();
31378     },
31379     
31380     refresh : function()
31381     {
31382         this.uploader.show();
31383         
31384         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31385             this.uploader.hide();
31386         }
31387         
31388         Roo.isTouch ? this.closable(false) : this.closable(true);
31389         
31390         this.fireEvent('refresh', this);
31391     },
31392     
31393     onRemove : function(e, el, o)
31394     {
31395         e.preventDefault();
31396         
31397         this.fireEvent('remove', this, o);
31398         
31399     },
31400     
31401     remove : function(o)
31402     {
31403         var files = [];
31404         
31405         Roo.each(this.files, function(file){
31406             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31407                 files.push(file);
31408                 return;
31409             }
31410
31411             o.target.remove();
31412
31413         }, this);
31414         
31415         this.files = files;
31416         
31417         this.refresh();
31418     },
31419     
31420     clear : function()
31421     {
31422         Roo.each(this.files, function(file){
31423             if(!file.target){
31424                 return;
31425             }
31426             
31427             file.target.remove();
31428
31429         }, this);
31430         
31431         this.files = [];
31432         
31433         this.refresh();
31434     },
31435     
31436     onClick : function(e, el, o)
31437     {
31438         e.preventDefault();
31439         
31440         this.fireEvent('click', this, o);
31441         
31442     },
31443     
31444     closable : function(closable)
31445     {
31446         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31447             
31448             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31449             
31450             if(closable){
31451                 el.show();
31452                 return;
31453             }
31454             
31455             el.hide();
31456             
31457         }, this);
31458     },
31459     
31460     xhrOnLoad : function(xhr)
31461     {
31462         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31463             el.remove();
31464         }, this);
31465         
31466         if (xhr.readyState !== 4) {
31467             this.arrange();
31468             this.fireEvent('exception', this, xhr);
31469             return;
31470         }
31471
31472         var response = Roo.decode(xhr.responseText);
31473         
31474         if(!response.success){
31475             this.arrange();
31476             this.fireEvent('exception', this, xhr);
31477             return;
31478         }
31479         
31480         var file = this.renderPreview(response.data);
31481         
31482         this.files.push(file);
31483         
31484         this.arrange();
31485         
31486         this.fireEvent('afterupload', this, xhr);
31487         
31488     },
31489     
31490     xhrOnError : function(xhr)
31491     {
31492         Roo.log('xhr on error');
31493         
31494         var response = Roo.decode(xhr.responseText);
31495           
31496         Roo.log(response);
31497         
31498         this.arrange();
31499     },
31500     
31501     process : function(file)
31502     {
31503         if(this.fireEvent('process', this, file) !== false){
31504             if(this.editable && file.type.indexOf('image') != -1){
31505                 this.fireEvent('edit', this, file);
31506                 return;
31507             }
31508
31509             this.uploadStart(file, false);
31510
31511             return;
31512         }
31513         
31514     },
31515     
31516     uploadStart : function(file, crop)
31517     {
31518         this.xhr = new XMLHttpRequest();
31519         
31520         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31521             this.arrange();
31522             return;
31523         }
31524         
31525         file.xhr = this.xhr;
31526             
31527         this.managerEl.createChild({
31528             tag : 'div',
31529             cls : 'roo-document-manager-loading',
31530             cn : [
31531                 {
31532                     tag : 'div',
31533                     tooltip : file.name,
31534                     cls : 'roo-document-manager-thumb',
31535                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31536                 }
31537             ]
31538
31539         });
31540
31541         this.xhr.open(this.method, this.url, true);
31542         
31543         var headers = {
31544             "Accept": "application/json",
31545             "Cache-Control": "no-cache",
31546             "X-Requested-With": "XMLHttpRequest"
31547         };
31548         
31549         for (var headerName in headers) {
31550             var headerValue = headers[headerName];
31551             if (headerValue) {
31552                 this.xhr.setRequestHeader(headerName, headerValue);
31553             }
31554         }
31555         
31556         var _this = this;
31557         
31558         this.xhr.onload = function()
31559         {
31560             _this.xhrOnLoad(_this.xhr);
31561         }
31562         
31563         this.xhr.onerror = function()
31564         {
31565             _this.xhrOnError(_this.xhr);
31566         }
31567         
31568         var formData = new FormData();
31569
31570         formData.append('returnHTML', 'NO');
31571         
31572         if(crop){
31573             formData.append('crop', crop);
31574         }
31575         
31576         formData.append(this.paramName, file, file.name);
31577         
31578         var options = {
31579             file : file, 
31580             manually : false
31581         };
31582         
31583         if(this.fireEvent('prepare', this, formData, options) != false){
31584             
31585             if(options.manually){
31586                 return;
31587             }
31588             
31589             this.xhr.send(formData);
31590             return;
31591         };
31592         
31593         this.uploadCancel();
31594     },
31595     
31596     uploadCancel : function()
31597     {
31598         if (this.xhr) {
31599             this.xhr.abort();
31600         }
31601         
31602         this.delegates = [];
31603         
31604         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31605             el.remove();
31606         }, this);
31607         
31608         this.arrange();
31609     },
31610     
31611     renderPreview : function(file)
31612     {
31613         if(typeof(file.target) != 'undefined' && file.target){
31614             return file;
31615         }
31616         
31617         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31618         
31619         var previewEl = this.managerEl.createChild({
31620             tag : 'div',
31621             cls : 'roo-document-manager-preview',
31622             cn : [
31623                 {
31624                     tag : 'div',
31625                     tooltip : file[this.toolTipName],
31626                     cls : 'roo-document-manager-thumb',
31627                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31628                 },
31629                 {
31630                     tag : 'button',
31631                     cls : 'close',
31632                     html : '<i class="fa fa-times-circle"></i>'
31633                 }
31634             ]
31635         });
31636
31637         var close = previewEl.select('button.close', true).first();
31638
31639         close.on('click', this.onRemove, this, file);
31640
31641         file.target = previewEl;
31642
31643         var image = previewEl.select('img', true).first();
31644         
31645         var _this = this;
31646         
31647         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31648         
31649         image.on('click', this.onClick, this, file);
31650         
31651         this.fireEvent('previewrendered', this, file);
31652         
31653         return file;
31654         
31655     },
31656     
31657     onPreviewLoad : function(file, image)
31658     {
31659         if(typeof(file.target) == 'undefined' || !file.target){
31660             return;
31661         }
31662         
31663         var width = image.dom.naturalWidth || image.dom.width;
31664         var height = image.dom.naturalHeight || image.dom.height;
31665         
31666         if(!this.previewResize) {
31667             return;
31668         }
31669         
31670         if(width > height){
31671             file.target.addClass('wide');
31672             return;
31673         }
31674         
31675         file.target.addClass('tall');
31676         return;
31677         
31678     },
31679     
31680     uploadFromSource : function(file, crop)
31681     {
31682         this.xhr = new XMLHttpRequest();
31683         
31684         this.managerEl.createChild({
31685             tag : 'div',
31686             cls : 'roo-document-manager-loading',
31687             cn : [
31688                 {
31689                     tag : 'div',
31690                     tooltip : file.name,
31691                     cls : 'roo-document-manager-thumb',
31692                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31693                 }
31694             ]
31695
31696         });
31697
31698         this.xhr.open(this.method, this.url, true);
31699         
31700         var headers = {
31701             "Accept": "application/json",
31702             "Cache-Control": "no-cache",
31703             "X-Requested-With": "XMLHttpRequest"
31704         };
31705         
31706         for (var headerName in headers) {
31707             var headerValue = headers[headerName];
31708             if (headerValue) {
31709                 this.xhr.setRequestHeader(headerName, headerValue);
31710             }
31711         }
31712         
31713         var _this = this;
31714         
31715         this.xhr.onload = function()
31716         {
31717             _this.xhrOnLoad(_this.xhr);
31718         }
31719         
31720         this.xhr.onerror = function()
31721         {
31722             _this.xhrOnError(_this.xhr);
31723         }
31724         
31725         var formData = new FormData();
31726
31727         formData.append('returnHTML', 'NO');
31728         
31729         formData.append('crop', crop);
31730         
31731         if(typeof(file.filename) != 'undefined'){
31732             formData.append('filename', file.filename);
31733         }
31734         
31735         if(typeof(file.mimetype) != 'undefined'){
31736             formData.append('mimetype', file.mimetype);
31737         }
31738         
31739         Roo.log(formData);
31740         
31741         if(this.fireEvent('prepare', this, formData) != false){
31742             this.xhr.send(formData);
31743         };
31744     }
31745 });
31746
31747 /*
31748 * Licence: LGPL
31749 */
31750
31751 /**
31752  * @class Roo.bootstrap.DocumentViewer
31753  * @extends Roo.bootstrap.Component
31754  * Bootstrap DocumentViewer class
31755  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31756  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31757  * 
31758  * @constructor
31759  * Create a new DocumentViewer
31760  * @param {Object} config The config object
31761  */
31762
31763 Roo.bootstrap.DocumentViewer = function(config){
31764     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31765     
31766     this.addEvents({
31767         /**
31768          * @event initial
31769          * Fire after initEvent
31770          * @param {Roo.bootstrap.DocumentViewer} this
31771          */
31772         "initial" : true,
31773         /**
31774          * @event click
31775          * Fire after click
31776          * @param {Roo.bootstrap.DocumentViewer} this
31777          */
31778         "click" : true,
31779         /**
31780          * @event download
31781          * Fire after download button
31782          * @param {Roo.bootstrap.DocumentViewer} this
31783          */
31784         "download" : true,
31785         /**
31786          * @event trash
31787          * Fire after trash button
31788          * @param {Roo.bootstrap.DocumentViewer} this
31789          */
31790         "trash" : true
31791         
31792     });
31793 };
31794
31795 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31796     
31797     showDownload : true,
31798     
31799     showTrash : true,
31800     
31801     getAutoCreate : function()
31802     {
31803         var cfg = {
31804             tag : 'div',
31805             cls : 'roo-document-viewer',
31806             cn : [
31807                 {
31808                     tag : 'div',
31809                     cls : 'roo-document-viewer-body',
31810                     cn : [
31811                         {
31812                             tag : 'div',
31813                             cls : 'roo-document-viewer-thumb',
31814                             cn : [
31815                                 {
31816                                     tag : 'img',
31817                                     cls : 'roo-document-viewer-image'
31818                                 }
31819                             ]
31820                         }
31821                     ]
31822                 },
31823                 {
31824                     tag : 'div',
31825                     cls : 'roo-document-viewer-footer',
31826                     cn : {
31827                         tag : 'div',
31828                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31829                         cn : [
31830                             {
31831                                 tag : 'div',
31832                                 cls : 'btn-group roo-document-viewer-download',
31833                                 cn : [
31834                                     {
31835                                         tag : 'button',
31836                                         cls : 'btn btn-default',
31837                                         html : '<i class="fa fa-download"></i>'
31838                                     }
31839                                 ]
31840                             },
31841                             {
31842                                 tag : 'div',
31843                                 cls : 'btn-group roo-document-viewer-trash',
31844                                 cn : [
31845                                     {
31846                                         tag : 'button',
31847                                         cls : 'btn btn-default',
31848                                         html : '<i class="fa fa-trash"></i>'
31849                                     }
31850                                 ]
31851                             }
31852                         ]
31853                     }
31854                 }
31855             ]
31856         };
31857         
31858         return cfg;
31859     },
31860     
31861     initEvents : function()
31862     {
31863         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31864         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31865         
31866         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31867         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31868         
31869         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31870         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31871         
31872         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31873         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31874         
31875         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31876         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31877         
31878         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31879         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31880         
31881         this.bodyEl.on('click', this.onClick, this);
31882         this.downloadBtn.on('click', this.onDownload, this);
31883         this.trashBtn.on('click', this.onTrash, this);
31884         
31885         this.downloadBtn.hide();
31886         this.trashBtn.hide();
31887         
31888         if(this.showDownload){
31889             this.downloadBtn.show();
31890         }
31891         
31892         if(this.showTrash){
31893             this.trashBtn.show();
31894         }
31895         
31896         if(!this.showDownload && !this.showTrash) {
31897             this.footerEl.hide();
31898         }
31899         
31900     },
31901     
31902     initial : function()
31903     {
31904         this.fireEvent('initial', this);
31905         
31906     },
31907     
31908     onClick : function(e)
31909     {
31910         e.preventDefault();
31911         
31912         this.fireEvent('click', this);
31913     },
31914     
31915     onDownload : function(e)
31916     {
31917         e.preventDefault();
31918         
31919         this.fireEvent('download', this);
31920     },
31921     
31922     onTrash : function(e)
31923     {
31924         e.preventDefault();
31925         
31926         this.fireEvent('trash', this);
31927     }
31928     
31929 });
31930 /*
31931  * - LGPL
31932  *
31933  * nav progress bar
31934  * 
31935  */
31936
31937 /**
31938  * @class Roo.bootstrap.NavProgressBar
31939  * @extends Roo.bootstrap.Component
31940  * Bootstrap NavProgressBar class
31941  * 
31942  * @constructor
31943  * Create a new nav progress bar
31944  * @param {Object} config The config object
31945  */
31946
31947 Roo.bootstrap.NavProgressBar = function(config){
31948     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31949
31950     this.bullets = this.bullets || [];
31951    
31952 //    Roo.bootstrap.NavProgressBar.register(this);
31953      this.addEvents({
31954         /**
31955              * @event changed
31956              * Fires when the active item changes
31957              * @param {Roo.bootstrap.NavProgressBar} this
31958              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31959              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31960          */
31961         'changed': true
31962      });
31963     
31964 };
31965
31966 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31967     
31968     bullets : [],
31969     barItems : [],
31970     
31971     getAutoCreate : function()
31972     {
31973         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31974         
31975         cfg = {
31976             tag : 'div',
31977             cls : 'roo-navigation-bar-group',
31978             cn : [
31979                 {
31980                     tag : 'div',
31981                     cls : 'roo-navigation-top-bar'
31982                 },
31983                 {
31984                     tag : 'div',
31985                     cls : 'roo-navigation-bullets-bar',
31986                     cn : [
31987                         {
31988                             tag : 'ul',
31989                             cls : 'roo-navigation-bar'
31990                         }
31991                     ]
31992                 },
31993                 
31994                 {
31995                     tag : 'div',
31996                     cls : 'roo-navigation-bottom-bar'
31997                 }
31998             ]
31999             
32000         };
32001         
32002         return cfg;
32003         
32004     },
32005     
32006     initEvents: function() 
32007     {
32008         
32009     },
32010     
32011     onRender : function(ct, position) 
32012     {
32013         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32014         
32015         if(this.bullets.length){
32016             Roo.each(this.bullets, function(b){
32017                this.addItem(b);
32018             }, this);
32019         }
32020         
32021         this.format();
32022         
32023     },
32024     
32025     addItem : function(cfg)
32026     {
32027         var item = new Roo.bootstrap.NavProgressItem(cfg);
32028         
32029         item.parentId = this.id;
32030         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32031         
32032         if(cfg.html){
32033             var top = new Roo.bootstrap.Element({
32034                 tag : 'div',
32035                 cls : 'roo-navigation-bar-text'
32036             });
32037             
32038             var bottom = new Roo.bootstrap.Element({
32039                 tag : 'div',
32040                 cls : 'roo-navigation-bar-text'
32041             });
32042             
32043             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32044             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32045             
32046             var topText = new Roo.bootstrap.Element({
32047                 tag : 'span',
32048                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32049             });
32050             
32051             var bottomText = new Roo.bootstrap.Element({
32052                 tag : 'span',
32053                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32054             });
32055             
32056             topText.onRender(top.el, null);
32057             bottomText.onRender(bottom.el, null);
32058             
32059             item.topEl = top;
32060             item.bottomEl = bottom;
32061         }
32062         
32063         this.barItems.push(item);
32064         
32065         return item;
32066     },
32067     
32068     getActive : function()
32069     {
32070         var active = false;
32071         
32072         Roo.each(this.barItems, function(v){
32073             
32074             if (!v.isActive()) {
32075                 return;
32076             }
32077             
32078             active = v;
32079             return false;
32080             
32081         });
32082         
32083         return active;
32084     },
32085     
32086     setActiveItem : function(item)
32087     {
32088         var prev = false;
32089         
32090         Roo.each(this.barItems, function(v){
32091             if (v.rid == item.rid) {
32092                 return ;
32093             }
32094             
32095             if (v.isActive()) {
32096                 v.setActive(false);
32097                 prev = v;
32098             }
32099         });
32100
32101         item.setActive(true);
32102         
32103         this.fireEvent('changed', this, item, prev);
32104     },
32105     
32106     getBarItem: function(rid)
32107     {
32108         var ret = false;
32109         
32110         Roo.each(this.barItems, function(e) {
32111             if (e.rid != rid) {
32112                 return;
32113             }
32114             
32115             ret =  e;
32116             return false;
32117         });
32118         
32119         return ret;
32120     },
32121     
32122     indexOfItem : function(item)
32123     {
32124         var index = false;
32125         
32126         Roo.each(this.barItems, function(v, i){
32127             
32128             if (v.rid != item.rid) {
32129                 return;
32130             }
32131             
32132             index = i;
32133             return false
32134         });
32135         
32136         return index;
32137     },
32138     
32139     setActiveNext : function()
32140     {
32141         var i = this.indexOfItem(this.getActive());
32142         
32143         if (i > this.barItems.length) {
32144             return;
32145         }
32146         
32147         this.setActiveItem(this.barItems[i+1]);
32148     },
32149     
32150     setActivePrev : function()
32151     {
32152         var i = this.indexOfItem(this.getActive());
32153         
32154         if (i  < 1) {
32155             return;
32156         }
32157         
32158         this.setActiveItem(this.barItems[i-1]);
32159     },
32160     
32161     format : function()
32162     {
32163         if(!this.barItems.length){
32164             return;
32165         }
32166      
32167         var width = 100 / this.barItems.length;
32168         
32169         Roo.each(this.barItems, function(i){
32170             i.el.setStyle('width', width + '%');
32171             i.topEl.el.setStyle('width', width + '%');
32172             i.bottomEl.el.setStyle('width', width + '%');
32173         }, this);
32174         
32175     }
32176     
32177 });
32178 /*
32179  * - LGPL
32180  *
32181  * Nav Progress Item
32182  * 
32183  */
32184
32185 /**
32186  * @class Roo.bootstrap.NavProgressItem
32187  * @extends Roo.bootstrap.Component
32188  * Bootstrap NavProgressItem class
32189  * @cfg {String} rid the reference id
32190  * @cfg {Boolean} active (true|false) Is item active default false
32191  * @cfg {Boolean} disabled (true|false) Is item active default false
32192  * @cfg {String} html
32193  * @cfg {String} position (top|bottom) text position default bottom
32194  * @cfg {String} icon show icon instead of number
32195  * 
32196  * @constructor
32197  * Create a new NavProgressItem
32198  * @param {Object} config The config object
32199  */
32200 Roo.bootstrap.NavProgressItem = function(config){
32201     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32202     this.addEvents({
32203         // raw events
32204         /**
32205          * @event click
32206          * The raw click event for the entire grid.
32207          * @param {Roo.bootstrap.NavProgressItem} this
32208          * @param {Roo.EventObject} e
32209          */
32210         "click" : true
32211     });
32212    
32213 };
32214
32215 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32216     
32217     rid : '',
32218     active : false,
32219     disabled : false,
32220     html : '',
32221     position : 'bottom',
32222     icon : false,
32223     
32224     getAutoCreate : function()
32225     {
32226         var iconCls = 'roo-navigation-bar-item-icon';
32227         
32228         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32229         
32230         var cfg = {
32231             tag: 'li',
32232             cls: 'roo-navigation-bar-item',
32233             cn : [
32234                 {
32235                     tag : 'i',
32236                     cls : iconCls
32237                 }
32238             ]
32239         };
32240         
32241         if(this.active){
32242             cfg.cls += ' active';
32243         }
32244         if(this.disabled){
32245             cfg.cls += ' disabled';
32246         }
32247         
32248         return cfg;
32249     },
32250     
32251     disable : function()
32252     {
32253         this.setDisabled(true);
32254     },
32255     
32256     enable : function()
32257     {
32258         this.setDisabled(false);
32259     },
32260     
32261     initEvents: function() 
32262     {
32263         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32264         
32265         this.iconEl.on('click', this.onClick, this);
32266     },
32267     
32268     onClick : function(e)
32269     {
32270         e.preventDefault();
32271         
32272         if(this.disabled){
32273             return;
32274         }
32275         
32276         if(this.fireEvent('click', this, e) === false){
32277             return;
32278         };
32279         
32280         this.parent().setActiveItem(this);
32281     },
32282     
32283     isActive: function () 
32284     {
32285         return this.active;
32286     },
32287     
32288     setActive : function(state)
32289     {
32290         if(this.active == state){
32291             return;
32292         }
32293         
32294         this.active = state;
32295         
32296         if (state) {
32297             this.el.addClass('active');
32298             return;
32299         }
32300         
32301         this.el.removeClass('active');
32302         
32303         return;
32304     },
32305     
32306     setDisabled : function(state)
32307     {
32308         if(this.disabled == state){
32309             return;
32310         }
32311         
32312         this.disabled = state;
32313         
32314         if (state) {
32315             this.el.addClass('disabled');
32316             return;
32317         }
32318         
32319         this.el.removeClass('disabled');
32320     },
32321     
32322     tooltipEl : function()
32323     {
32324         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32325     }
32326 });
32327  
32328
32329  /*
32330  * - LGPL
32331  *
32332  * FieldLabel
32333  * 
32334  */
32335
32336 /**
32337  * @class Roo.bootstrap.FieldLabel
32338  * @extends Roo.bootstrap.Component
32339  * Bootstrap FieldLabel class
32340  * @cfg {String} html contents of the element
32341  * @cfg {String} tag tag of the element default label
32342  * @cfg {String} cls class of the element
32343  * @cfg {String} target label target 
32344  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32345  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32346  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32347  * @cfg {String} iconTooltip default "This field is required"
32348  * @cfg {String} indicatorpos (left|right) default left
32349  * 
32350  * @constructor
32351  * Create a new FieldLabel
32352  * @param {Object} config The config object
32353  */
32354
32355 Roo.bootstrap.FieldLabel = function(config){
32356     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32357     
32358     this.addEvents({
32359             /**
32360              * @event invalid
32361              * Fires after the field has been marked as invalid.
32362              * @param {Roo.form.FieldLabel} this
32363              * @param {String} msg The validation message
32364              */
32365             invalid : true,
32366             /**
32367              * @event valid
32368              * Fires after the field has been validated with no errors.
32369              * @param {Roo.form.FieldLabel} this
32370              */
32371             valid : true
32372         });
32373 };
32374
32375 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32376     
32377     tag: 'label',
32378     cls: '',
32379     html: '',
32380     target: '',
32381     allowBlank : true,
32382     invalidClass : 'has-warning',
32383     validClass : 'has-success',
32384     iconTooltip : 'This field is required',
32385     indicatorpos : 'left',
32386     
32387     getAutoCreate : function(){
32388         
32389         var cls = "";
32390         if (!this.allowBlank) {
32391             cls  = "visible";
32392         }
32393         
32394         var cfg = {
32395             tag : this.tag,
32396             cls : 'roo-bootstrap-field-label ' + this.cls,
32397             for : this.target,
32398             cn : [
32399                 {
32400                     tag : 'i',
32401                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32402                     tooltip : this.iconTooltip
32403                 },
32404                 {
32405                     tag : 'span',
32406                     html : this.html
32407                 }
32408             ] 
32409         };
32410         
32411         if(this.indicatorpos == 'right'){
32412             var cfg = {
32413                 tag : this.tag,
32414                 cls : 'roo-bootstrap-field-label ' + this.cls,
32415                 for : this.target,
32416                 cn : [
32417                     {
32418                         tag : 'span',
32419                         html : this.html
32420                     },
32421                     {
32422                         tag : 'i',
32423                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32424                         tooltip : this.iconTooltip
32425                     }
32426                 ] 
32427             };
32428         }
32429         
32430         return cfg;
32431     },
32432     
32433     initEvents: function() 
32434     {
32435         Roo.bootstrap.Element.superclass.initEvents.call(this);
32436         
32437         this.indicator = this.indicatorEl();
32438         
32439         if(this.indicator){
32440             this.indicator.removeClass('visible');
32441             this.indicator.addClass('invisible');
32442         }
32443         
32444         Roo.bootstrap.FieldLabel.register(this);
32445     },
32446     
32447     indicatorEl : function()
32448     {
32449         var indicator = this.el.select('i.roo-required-indicator',true).first();
32450         
32451         if(!indicator){
32452             return false;
32453         }
32454         
32455         return indicator;
32456         
32457     },
32458     
32459     /**
32460      * Mark this field as valid
32461      */
32462     markValid : function()
32463     {
32464         if(this.indicator){
32465             this.indicator.removeClass('visible');
32466             this.indicator.addClass('invisible');
32467         }
32468         if (Roo.bootstrap.version == 3) {
32469             this.el.removeClass(this.invalidClass);
32470             this.el.addClass(this.validClass);
32471         } else {
32472             this.el.removeClass('is-invalid');
32473             this.el.addClass('is-valid');
32474         }
32475         
32476         
32477         this.fireEvent('valid', this);
32478     },
32479     
32480     /**
32481      * Mark this field as invalid
32482      * @param {String} msg The validation message
32483      */
32484     markInvalid : function(msg)
32485     {
32486         if(this.indicator){
32487             this.indicator.removeClass('invisible');
32488             this.indicator.addClass('visible');
32489         }
32490           if (Roo.bootstrap.version == 3) {
32491             this.el.removeClass(this.validClass);
32492             this.el.addClass(this.invalidClass);
32493         } else {
32494             this.el.removeClass('is-valid');
32495             this.el.addClass('is-invalid');
32496         }
32497         
32498         
32499         this.fireEvent('invalid', this, msg);
32500     }
32501     
32502    
32503 });
32504
32505 Roo.apply(Roo.bootstrap.FieldLabel, {
32506     
32507     groups: {},
32508     
32509      /**
32510     * register a FieldLabel Group
32511     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32512     */
32513     register : function(label)
32514     {
32515         if(this.groups.hasOwnProperty(label.target)){
32516             return;
32517         }
32518      
32519         this.groups[label.target] = label;
32520         
32521     },
32522     /**
32523     * fetch a FieldLabel Group based on the target
32524     * @param {string} target
32525     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32526     */
32527     get: function(target) {
32528         if (typeof(this.groups[target]) == 'undefined') {
32529             return false;
32530         }
32531         
32532         return this.groups[target] ;
32533     }
32534 });
32535
32536  
32537
32538  /*
32539  * - LGPL
32540  *
32541  * page DateSplitField.
32542  * 
32543  */
32544
32545
32546 /**
32547  * @class Roo.bootstrap.DateSplitField
32548  * @extends Roo.bootstrap.Component
32549  * Bootstrap DateSplitField class
32550  * @cfg {string} fieldLabel - the label associated
32551  * @cfg {Number} labelWidth set the width of label (0-12)
32552  * @cfg {String} labelAlign (top|left)
32553  * @cfg {Boolean} dayAllowBlank (true|false) default false
32554  * @cfg {Boolean} monthAllowBlank (true|false) default false
32555  * @cfg {Boolean} yearAllowBlank (true|false) default false
32556  * @cfg {string} dayPlaceholder 
32557  * @cfg {string} monthPlaceholder
32558  * @cfg {string} yearPlaceholder
32559  * @cfg {string} dayFormat default 'd'
32560  * @cfg {string} monthFormat default 'm'
32561  * @cfg {string} yearFormat default 'Y'
32562  * @cfg {Number} labellg set the width of label (1-12)
32563  * @cfg {Number} labelmd set the width of label (1-12)
32564  * @cfg {Number} labelsm set the width of label (1-12)
32565  * @cfg {Number} labelxs set the width of label (1-12)
32566
32567  *     
32568  * @constructor
32569  * Create a new DateSplitField
32570  * @param {Object} config The config object
32571  */
32572
32573 Roo.bootstrap.DateSplitField = function(config){
32574     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32575     
32576     this.addEvents({
32577         // raw events
32578          /**
32579          * @event years
32580          * getting the data of years
32581          * @param {Roo.bootstrap.DateSplitField} this
32582          * @param {Object} years
32583          */
32584         "years" : true,
32585         /**
32586          * @event days
32587          * getting the data of days
32588          * @param {Roo.bootstrap.DateSplitField} this
32589          * @param {Object} days
32590          */
32591         "days" : true,
32592         /**
32593          * @event invalid
32594          * Fires after the field has been marked as invalid.
32595          * @param {Roo.form.Field} this
32596          * @param {String} msg The validation message
32597          */
32598         invalid : true,
32599        /**
32600          * @event valid
32601          * Fires after the field has been validated with no errors.
32602          * @param {Roo.form.Field} this
32603          */
32604         valid : true
32605     });
32606 };
32607
32608 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32609     
32610     fieldLabel : '',
32611     labelAlign : 'top',
32612     labelWidth : 3,
32613     dayAllowBlank : false,
32614     monthAllowBlank : false,
32615     yearAllowBlank : false,
32616     dayPlaceholder : '',
32617     monthPlaceholder : '',
32618     yearPlaceholder : '',
32619     dayFormat : 'd',
32620     monthFormat : 'm',
32621     yearFormat : 'Y',
32622     isFormField : true,
32623     labellg : 0,
32624     labelmd : 0,
32625     labelsm : 0,
32626     labelxs : 0,
32627     
32628     getAutoCreate : function()
32629     {
32630         var cfg = {
32631             tag : 'div',
32632             cls : 'row roo-date-split-field-group',
32633             cn : [
32634                 {
32635                     tag : 'input',
32636                     type : 'hidden',
32637                     cls : 'form-hidden-field roo-date-split-field-group-value',
32638                     name : this.name
32639                 }
32640             ]
32641         };
32642         
32643         var labelCls = 'col-md-12';
32644         var contentCls = 'col-md-4';
32645         
32646         if(this.fieldLabel){
32647             
32648             var label = {
32649                 tag : 'div',
32650                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32651                 cn : [
32652                     {
32653                         tag : 'label',
32654                         html : this.fieldLabel
32655                     }
32656                 ]
32657             };
32658             
32659             if(this.labelAlign == 'left'){
32660             
32661                 if(this.labelWidth > 12){
32662                     label.style = "width: " + this.labelWidth + 'px';
32663                 }
32664
32665                 if(this.labelWidth < 13 && this.labelmd == 0){
32666                     this.labelmd = this.labelWidth;
32667                 }
32668
32669                 if(this.labellg > 0){
32670                     labelCls = ' col-lg-' + this.labellg;
32671                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32672                 }
32673
32674                 if(this.labelmd > 0){
32675                     labelCls = ' col-md-' + this.labelmd;
32676                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32677                 }
32678
32679                 if(this.labelsm > 0){
32680                     labelCls = ' col-sm-' + this.labelsm;
32681                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32682                 }
32683
32684                 if(this.labelxs > 0){
32685                     labelCls = ' col-xs-' + this.labelxs;
32686                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32687                 }
32688             }
32689             
32690             label.cls += ' ' + labelCls;
32691             
32692             cfg.cn.push(label);
32693         }
32694         
32695         Roo.each(['day', 'month', 'year'], function(t){
32696             cfg.cn.push({
32697                 tag : 'div',
32698                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32699             });
32700         }, this);
32701         
32702         return cfg;
32703     },
32704     
32705     inputEl: function ()
32706     {
32707         return this.el.select('.roo-date-split-field-group-value', true).first();
32708     },
32709     
32710     onRender : function(ct, position) 
32711     {
32712         var _this = this;
32713         
32714         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32715         
32716         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32717         
32718         this.dayField = new Roo.bootstrap.ComboBox({
32719             allowBlank : this.dayAllowBlank,
32720             alwaysQuery : true,
32721             displayField : 'value',
32722             editable : false,
32723             fieldLabel : '',
32724             forceSelection : true,
32725             mode : 'local',
32726             placeholder : this.dayPlaceholder,
32727             selectOnFocus : true,
32728             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32729             triggerAction : 'all',
32730             typeAhead : true,
32731             valueField : 'value',
32732             store : new Roo.data.SimpleStore({
32733                 data : (function() {    
32734                     var days = [];
32735                     _this.fireEvent('days', _this, days);
32736                     return days;
32737                 })(),
32738                 fields : [ 'value' ]
32739             }),
32740             listeners : {
32741                 select : function (_self, record, index)
32742                 {
32743                     _this.setValue(_this.getValue());
32744                 }
32745             }
32746         });
32747
32748         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32749         
32750         this.monthField = new Roo.bootstrap.MonthField({
32751             after : '<i class=\"fa fa-calendar\"></i>',
32752             allowBlank : this.monthAllowBlank,
32753             placeholder : this.monthPlaceholder,
32754             readOnly : true,
32755             listeners : {
32756                 render : function (_self)
32757                 {
32758                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32759                         e.preventDefault();
32760                         _self.focus();
32761                     });
32762                 },
32763                 select : function (_self, oldvalue, newvalue)
32764                 {
32765                     _this.setValue(_this.getValue());
32766                 }
32767             }
32768         });
32769         
32770         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32771         
32772         this.yearField = new Roo.bootstrap.ComboBox({
32773             allowBlank : this.yearAllowBlank,
32774             alwaysQuery : true,
32775             displayField : 'value',
32776             editable : false,
32777             fieldLabel : '',
32778             forceSelection : true,
32779             mode : 'local',
32780             placeholder : this.yearPlaceholder,
32781             selectOnFocus : true,
32782             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32783             triggerAction : 'all',
32784             typeAhead : true,
32785             valueField : 'value',
32786             store : new Roo.data.SimpleStore({
32787                 data : (function() {
32788                     var years = [];
32789                     _this.fireEvent('years', _this, years);
32790                     return years;
32791                 })(),
32792                 fields : [ 'value' ]
32793             }),
32794             listeners : {
32795                 select : function (_self, record, index)
32796                 {
32797                     _this.setValue(_this.getValue());
32798                 }
32799             }
32800         });
32801
32802         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32803     },
32804     
32805     setValue : function(v, format)
32806     {
32807         this.inputEl.dom.value = v;
32808         
32809         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32810         
32811         var d = Date.parseDate(v, f);
32812         
32813         if(!d){
32814             this.validate();
32815             return;
32816         }
32817         
32818         this.setDay(d.format(this.dayFormat));
32819         this.setMonth(d.format(this.monthFormat));
32820         this.setYear(d.format(this.yearFormat));
32821         
32822         this.validate();
32823         
32824         return;
32825     },
32826     
32827     setDay : function(v)
32828     {
32829         this.dayField.setValue(v);
32830         this.inputEl.dom.value = this.getValue();
32831         this.validate();
32832         return;
32833     },
32834     
32835     setMonth : function(v)
32836     {
32837         this.monthField.setValue(v, true);
32838         this.inputEl.dom.value = this.getValue();
32839         this.validate();
32840         return;
32841     },
32842     
32843     setYear : function(v)
32844     {
32845         this.yearField.setValue(v);
32846         this.inputEl.dom.value = this.getValue();
32847         this.validate();
32848         return;
32849     },
32850     
32851     getDay : function()
32852     {
32853         return this.dayField.getValue();
32854     },
32855     
32856     getMonth : function()
32857     {
32858         return this.monthField.getValue();
32859     },
32860     
32861     getYear : function()
32862     {
32863         return this.yearField.getValue();
32864     },
32865     
32866     getValue : function()
32867     {
32868         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32869         
32870         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32871         
32872         return date;
32873     },
32874     
32875     reset : function()
32876     {
32877         this.setDay('');
32878         this.setMonth('');
32879         this.setYear('');
32880         this.inputEl.dom.value = '';
32881         this.validate();
32882         return;
32883     },
32884     
32885     validate : function()
32886     {
32887         var d = this.dayField.validate();
32888         var m = this.monthField.validate();
32889         var y = this.yearField.validate();
32890         
32891         var valid = true;
32892         
32893         if(
32894                 (!this.dayAllowBlank && !d) ||
32895                 (!this.monthAllowBlank && !m) ||
32896                 (!this.yearAllowBlank && !y)
32897         ){
32898             valid = false;
32899         }
32900         
32901         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32902             return valid;
32903         }
32904         
32905         if(valid){
32906             this.markValid();
32907             return valid;
32908         }
32909         
32910         this.markInvalid();
32911         
32912         return valid;
32913     },
32914     
32915     markValid : function()
32916     {
32917         
32918         var label = this.el.select('label', true).first();
32919         var icon = this.el.select('i.fa-star', true).first();
32920
32921         if(label && icon){
32922             icon.remove();
32923         }
32924         
32925         this.fireEvent('valid', this);
32926     },
32927     
32928      /**
32929      * Mark this field as invalid
32930      * @param {String} msg The validation message
32931      */
32932     markInvalid : function(msg)
32933     {
32934         
32935         var label = this.el.select('label', true).first();
32936         var icon = this.el.select('i.fa-star', true).first();
32937
32938         if(label && !icon){
32939             this.el.select('.roo-date-split-field-label', true).createChild({
32940                 tag : 'i',
32941                 cls : 'text-danger fa fa-lg fa-star',
32942                 tooltip : 'This field is required',
32943                 style : 'margin-right:5px;'
32944             }, label, true);
32945         }
32946         
32947         this.fireEvent('invalid', this, msg);
32948     },
32949     
32950     clearInvalid : function()
32951     {
32952         var label = this.el.select('label', true).first();
32953         var icon = this.el.select('i.fa-star', true).first();
32954
32955         if(label && icon){
32956             icon.remove();
32957         }
32958         
32959         this.fireEvent('valid', this);
32960     },
32961     
32962     getName: function()
32963     {
32964         return this.name;
32965     }
32966     
32967 });
32968
32969  /**
32970  *
32971  * This is based on 
32972  * http://masonry.desandro.com
32973  *
32974  * The idea is to render all the bricks based on vertical width...
32975  *
32976  * The original code extends 'outlayer' - we might need to use that....
32977  * 
32978  */
32979
32980
32981 /**
32982  * @class Roo.bootstrap.LayoutMasonry
32983  * @extends Roo.bootstrap.Component
32984  * Bootstrap Layout Masonry class
32985  * 
32986  * @constructor
32987  * Create a new Element
32988  * @param {Object} config The config object
32989  */
32990
32991 Roo.bootstrap.LayoutMasonry = function(config){
32992     
32993     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32994     
32995     this.bricks = [];
32996     
32997     Roo.bootstrap.LayoutMasonry.register(this);
32998     
32999     this.addEvents({
33000         // raw events
33001         /**
33002          * @event layout
33003          * Fire after layout the items
33004          * @param {Roo.bootstrap.LayoutMasonry} this
33005          * @param {Roo.EventObject} e
33006          */
33007         "layout" : true
33008     });
33009     
33010 };
33011
33012 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33013     
33014     /**
33015      * @cfg {Boolean} isLayoutInstant = no animation?
33016      */   
33017     isLayoutInstant : false, // needed?
33018    
33019     /**
33020      * @cfg {Number} boxWidth  width of the columns
33021      */   
33022     boxWidth : 450,
33023     
33024       /**
33025      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33026      */   
33027     boxHeight : 0,
33028     
33029     /**
33030      * @cfg {Number} padWidth padding below box..
33031      */   
33032     padWidth : 10, 
33033     
33034     /**
33035      * @cfg {Number} gutter gutter width..
33036      */   
33037     gutter : 10,
33038     
33039      /**
33040      * @cfg {Number} maxCols maximum number of columns
33041      */   
33042     
33043     maxCols: 0,
33044     
33045     /**
33046      * @cfg {Boolean} isAutoInitial defalut true
33047      */   
33048     isAutoInitial : true, 
33049     
33050     containerWidth: 0,
33051     
33052     /**
33053      * @cfg {Boolean} isHorizontal defalut false
33054      */   
33055     isHorizontal : false, 
33056
33057     currentSize : null,
33058     
33059     tag: 'div',
33060     
33061     cls: '',
33062     
33063     bricks: null, //CompositeElement
33064     
33065     cols : 1,
33066     
33067     _isLayoutInited : false,
33068     
33069 //    isAlternative : false, // only use for vertical layout...
33070     
33071     /**
33072      * @cfg {Number} alternativePadWidth padding below box..
33073      */   
33074     alternativePadWidth : 50,
33075     
33076     selectedBrick : [],
33077     
33078     getAutoCreate : function(){
33079         
33080         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33081         
33082         var cfg = {
33083             tag: this.tag,
33084             cls: 'blog-masonary-wrapper ' + this.cls,
33085             cn : {
33086                 cls : 'mas-boxes masonary'
33087             }
33088         };
33089         
33090         return cfg;
33091     },
33092     
33093     getChildContainer: function( )
33094     {
33095         if (this.boxesEl) {
33096             return this.boxesEl;
33097         }
33098         
33099         this.boxesEl = this.el.select('.mas-boxes').first();
33100         
33101         return this.boxesEl;
33102     },
33103     
33104     
33105     initEvents : function()
33106     {
33107         var _this = this;
33108         
33109         if(this.isAutoInitial){
33110             Roo.log('hook children rendered');
33111             this.on('childrenrendered', function() {
33112                 Roo.log('children rendered');
33113                 _this.initial();
33114             } ,this);
33115         }
33116     },
33117     
33118     initial : function()
33119     {
33120         this.selectedBrick = [];
33121         
33122         this.currentSize = this.el.getBox(true);
33123         
33124         Roo.EventManager.onWindowResize(this.resize, this); 
33125
33126         if(!this.isAutoInitial){
33127             this.layout();
33128             return;
33129         }
33130         
33131         this.layout();
33132         
33133         return;
33134         //this.layout.defer(500,this);
33135         
33136     },
33137     
33138     resize : function()
33139     {
33140         var cs = this.el.getBox(true);
33141         
33142         if (
33143                 this.currentSize.width == cs.width && 
33144                 this.currentSize.x == cs.x && 
33145                 this.currentSize.height == cs.height && 
33146                 this.currentSize.y == cs.y 
33147         ) {
33148             Roo.log("no change in with or X or Y");
33149             return;
33150         }
33151         
33152         this.currentSize = cs;
33153         
33154         this.layout();
33155         
33156     },
33157     
33158     layout : function()
33159     {   
33160         this._resetLayout();
33161         
33162         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33163         
33164         this.layoutItems( isInstant );
33165       
33166         this._isLayoutInited = true;
33167         
33168         this.fireEvent('layout', this);
33169         
33170     },
33171     
33172     _resetLayout : function()
33173     {
33174         if(this.isHorizontal){
33175             this.horizontalMeasureColumns();
33176             return;
33177         }
33178         
33179         this.verticalMeasureColumns();
33180         
33181     },
33182     
33183     verticalMeasureColumns : function()
33184     {
33185         this.getContainerWidth();
33186         
33187 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33188 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33189 //            return;
33190 //        }
33191         
33192         var boxWidth = this.boxWidth + this.padWidth;
33193         
33194         if(this.containerWidth < this.boxWidth){
33195             boxWidth = this.containerWidth
33196         }
33197         
33198         var containerWidth = this.containerWidth;
33199         
33200         var cols = Math.floor(containerWidth / boxWidth);
33201         
33202         this.cols = Math.max( cols, 1 );
33203         
33204         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33205         
33206         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33207         
33208         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33209         
33210         this.colWidth = boxWidth + avail - this.padWidth;
33211         
33212         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33213         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33214     },
33215     
33216     horizontalMeasureColumns : function()
33217     {
33218         this.getContainerWidth();
33219         
33220         var boxWidth = this.boxWidth;
33221         
33222         if(this.containerWidth < boxWidth){
33223             boxWidth = this.containerWidth;
33224         }
33225         
33226         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33227         
33228         this.el.setHeight(boxWidth);
33229         
33230     },
33231     
33232     getContainerWidth : function()
33233     {
33234         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33235     },
33236     
33237     layoutItems : function( isInstant )
33238     {
33239         Roo.log(this.bricks);
33240         
33241         var items = Roo.apply([], this.bricks);
33242         
33243         if(this.isHorizontal){
33244             this._horizontalLayoutItems( items , isInstant );
33245             return;
33246         }
33247         
33248 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33249 //            this._verticalAlternativeLayoutItems( items , isInstant );
33250 //            return;
33251 //        }
33252         
33253         this._verticalLayoutItems( items , isInstant );
33254         
33255     },
33256     
33257     _verticalLayoutItems : function ( items , isInstant)
33258     {
33259         if ( !items || !items.length ) {
33260             return;
33261         }
33262         
33263         var standard = [
33264             ['xs', 'xs', 'xs', 'tall'],
33265             ['xs', 'xs', 'tall'],
33266             ['xs', 'xs', 'sm'],
33267             ['xs', 'xs', 'xs'],
33268             ['xs', 'tall'],
33269             ['xs', 'sm'],
33270             ['xs', 'xs'],
33271             ['xs'],
33272             
33273             ['sm', 'xs', 'xs'],
33274             ['sm', 'xs'],
33275             ['sm'],
33276             
33277             ['tall', 'xs', 'xs', 'xs'],
33278             ['tall', 'xs', 'xs'],
33279             ['tall', 'xs'],
33280             ['tall']
33281             
33282         ];
33283         
33284         var queue = [];
33285         
33286         var boxes = [];
33287         
33288         var box = [];
33289         
33290         Roo.each(items, function(item, k){
33291             
33292             switch (item.size) {
33293                 // these layouts take up a full box,
33294                 case 'md' :
33295                 case 'md-left' :
33296                 case 'md-right' :
33297                 case 'wide' :
33298                     
33299                     if(box.length){
33300                         boxes.push(box);
33301                         box = [];
33302                     }
33303                     
33304                     boxes.push([item]);
33305                     
33306                     break;
33307                     
33308                 case 'xs' :
33309                 case 'sm' :
33310                 case 'tall' :
33311                     
33312                     box.push(item);
33313                     
33314                     break;
33315                 default :
33316                     break;
33317                     
33318             }
33319             
33320         }, this);
33321         
33322         if(box.length){
33323             boxes.push(box);
33324             box = [];
33325         }
33326         
33327         var filterPattern = function(box, length)
33328         {
33329             if(!box.length){
33330                 return;
33331             }
33332             
33333             var match = false;
33334             
33335             var pattern = box.slice(0, length);
33336             
33337             var format = [];
33338             
33339             Roo.each(pattern, function(i){
33340                 format.push(i.size);
33341             }, this);
33342             
33343             Roo.each(standard, function(s){
33344                 
33345                 if(String(s) != String(format)){
33346                     return;
33347                 }
33348                 
33349                 match = true;
33350                 return false;
33351                 
33352             }, this);
33353             
33354             if(!match && length == 1){
33355                 return;
33356             }
33357             
33358             if(!match){
33359                 filterPattern(box, length - 1);
33360                 return;
33361             }
33362                 
33363             queue.push(pattern);
33364
33365             box = box.slice(length, box.length);
33366
33367             filterPattern(box, 4);
33368
33369             return;
33370             
33371         }
33372         
33373         Roo.each(boxes, function(box, k){
33374             
33375             if(!box.length){
33376                 return;
33377             }
33378             
33379             if(box.length == 1){
33380                 queue.push(box);
33381                 return;
33382             }
33383             
33384             filterPattern(box, 4);
33385             
33386         }, this);
33387         
33388         this._processVerticalLayoutQueue( queue, isInstant );
33389         
33390     },
33391     
33392 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33393 //    {
33394 //        if ( !items || !items.length ) {
33395 //            return;
33396 //        }
33397 //
33398 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33399 //        
33400 //    },
33401     
33402     _horizontalLayoutItems : function ( items , isInstant)
33403     {
33404         if ( !items || !items.length || items.length < 3) {
33405             return;
33406         }
33407         
33408         items.reverse();
33409         
33410         var eItems = items.slice(0, 3);
33411         
33412         items = items.slice(3, items.length);
33413         
33414         var standard = [
33415             ['xs', 'xs', 'xs', 'wide'],
33416             ['xs', 'xs', 'wide'],
33417             ['xs', 'xs', 'sm'],
33418             ['xs', 'xs', 'xs'],
33419             ['xs', 'wide'],
33420             ['xs', 'sm'],
33421             ['xs', 'xs'],
33422             ['xs'],
33423             
33424             ['sm', 'xs', 'xs'],
33425             ['sm', 'xs'],
33426             ['sm'],
33427             
33428             ['wide', 'xs', 'xs', 'xs'],
33429             ['wide', 'xs', 'xs'],
33430             ['wide', 'xs'],
33431             ['wide'],
33432             
33433             ['wide-thin']
33434         ];
33435         
33436         var queue = [];
33437         
33438         var boxes = [];
33439         
33440         var box = [];
33441         
33442         Roo.each(items, function(item, k){
33443             
33444             switch (item.size) {
33445                 case 'md' :
33446                 case 'md-left' :
33447                 case 'md-right' :
33448                 case 'tall' :
33449                     
33450                     if(box.length){
33451                         boxes.push(box);
33452                         box = [];
33453                     }
33454                     
33455                     boxes.push([item]);
33456                     
33457                     break;
33458                     
33459                 case 'xs' :
33460                 case 'sm' :
33461                 case 'wide' :
33462                 case 'wide-thin' :
33463                     
33464                     box.push(item);
33465                     
33466                     break;
33467                 default :
33468                     break;
33469                     
33470             }
33471             
33472         }, this);
33473         
33474         if(box.length){
33475             boxes.push(box);
33476             box = [];
33477         }
33478         
33479         var filterPattern = function(box, length)
33480         {
33481             if(!box.length){
33482                 return;
33483             }
33484             
33485             var match = false;
33486             
33487             var pattern = box.slice(0, length);
33488             
33489             var format = [];
33490             
33491             Roo.each(pattern, function(i){
33492                 format.push(i.size);
33493             }, this);
33494             
33495             Roo.each(standard, function(s){
33496                 
33497                 if(String(s) != String(format)){
33498                     return;
33499                 }
33500                 
33501                 match = true;
33502                 return false;
33503                 
33504             }, this);
33505             
33506             if(!match && length == 1){
33507                 return;
33508             }
33509             
33510             if(!match){
33511                 filterPattern(box, length - 1);
33512                 return;
33513             }
33514                 
33515             queue.push(pattern);
33516
33517             box = box.slice(length, box.length);
33518
33519             filterPattern(box, 4);
33520
33521             return;
33522             
33523         }
33524         
33525         Roo.each(boxes, function(box, k){
33526             
33527             if(!box.length){
33528                 return;
33529             }
33530             
33531             if(box.length == 1){
33532                 queue.push(box);
33533                 return;
33534             }
33535             
33536             filterPattern(box, 4);
33537             
33538         }, this);
33539         
33540         
33541         var prune = [];
33542         
33543         var pos = this.el.getBox(true);
33544         
33545         var minX = pos.x;
33546         
33547         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33548         
33549         var hit_end = false;
33550         
33551         Roo.each(queue, function(box){
33552             
33553             if(hit_end){
33554                 
33555                 Roo.each(box, function(b){
33556                 
33557                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33558                     b.el.hide();
33559
33560                 }, this);
33561
33562                 return;
33563             }
33564             
33565             var mx = 0;
33566             
33567             Roo.each(box, function(b){
33568                 
33569                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33570                 b.el.show();
33571
33572                 mx = Math.max(mx, b.x);
33573                 
33574             }, this);
33575             
33576             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33577             
33578             if(maxX < minX){
33579                 
33580                 Roo.each(box, function(b){
33581                 
33582                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33583                     b.el.hide();
33584                     
33585                 }, this);
33586                 
33587                 hit_end = true;
33588                 
33589                 return;
33590             }
33591             
33592             prune.push(box);
33593             
33594         }, this);
33595         
33596         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33597     },
33598     
33599     /** Sets position of item in DOM
33600     * @param {Element} item
33601     * @param {Number} x - horizontal position
33602     * @param {Number} y - vertical position
33603     * @param {Boolean} isInstant - disables transitions
33604     */
33605     _processVerticalLayoutQueue : function( queue, isInstant )
33606     {
33607         var pos = this.el.getBox(true);
33608         var x = pos.x;
33609         var y = pos.y;
33610         var maxY = [];
33611         
33612         for (var i = 0; i < this.cols; i++){
33613             maxY[i] = pos.y;
33614         }
33615         
33616         Roo.each(queue, function(box, k){
33617             
33618             var col = k % this.cols;
33619             
33620             Roo.each(box, function(b,kk){
33621                 
33622                 b.el.position('absolute');
33623                 
33624                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33625                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33626                 
33627                 if(b.size == 'md-left' || b.size == 'md-right'){
33628                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33629                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33630                 }
33631                 
33632                 b.el.setWidth(width);
33633                 b.el.setHeight(height);
33634                 // iframe?
33635                 b.el.select('iframe',true).setSize(width,height);
33636                 
33637             }, this);
33638             
33639             for (var i = 0; i < this.cols; i++){
33640                 
33641                 if(maxY[i] < maxY[col]){
33642                     col = i;
33643                     continue;
33644                 }
33645                 
33646                 col = Math.min(col, i);
33647                 
33648             }
33649             
33650             x = pos.x + col * (this.colWidth + this.padWidth);
33651             
33652             y = maxY[col];
33653             
33654             var positions = [];
33655             
33656             switch (box.length){
33657                 case 1 :
33658                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33659                     break;
33660                 case 2 :
33661                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33662                     break;
33663                 case 3 :
33664                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33665                     break;
33666                 case 4 :
33667                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33668                     break;
33669                 default :
33670                     break;
33671             }
33672             
33673             Roo.each(box, function(b,kk){
33674                 
33675                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33676                 
33677                 var sz = b.el.getSize();
33678                 
33679                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33680                 
33681             }, this);
33682             
33683         }, this);
33684         
33685         var mY = 0;
33686         
33687         for (var i = 0; i < this.cols; i++){
33688             mY = Math.max(mY, maxY[i]);
33689         }
33690         
33691         this.el.setHeight(mY - pos.y);
33692         
33693     },
33694     
33695 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33696 //    {
33697 //        var pos = this.el.getBox(true);
33698 //        var x = pos.x;
33699 //        var y = pos.y;
33700 //        var maxX = pos.right;
33701 //        
33702 //        var maxHeight = 0;
33703 //        
33704 //        Roo.each(items, function(item, k){
33705 //            
33706 //            var c = k % 2;
33707 //            
33708 //            item.el.position('absolute');
33709 //                
33710 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33711 //
33712 //            item.el.setWidth(width);
33713 //
33714 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33715 //
33716 //            item.el.setHeight(height);
33717 //            
33718 //            if(c == 0){
33719 //                item.el.setXY([x, y], isInstant ? false : true);
33720 //            } else {
33721 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33722 //            }
33723 //            
33724 //            y = y + height + this.alternativePadWidth;
33725 //            
33726 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33727 //            
33728 //        }, this);
33729 //        
33730 //        this.el.setHeight(maxHeight);
33731 //        
33732 //    },
33733     
33734     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33735     {
33736         var pos = this.el.getBox(true);
33737         
33738         var minX = pos.x;
33739         var minY = pos.y;
33740         
33741         var maxX = pos.right;
33742         
33743         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33744         
33745         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33746         
33747         Roo.each(queue, function(box, k){
33748             
33749             Roo.each(box, function(b, kk){
33750                 
33751                 b.el.position('absolute');
33752                 
33753                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33754                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33755                 
33756                 if(b.size == 'md-left' || b.size == 'md-right'){
33757                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33758                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33759                 }
33760                 
33761                 b.el.setWidth(width);
33762                 b.el.setHeight(height);
33763                 
33764             }, this);
33765             
33766             if(!box.length){
33767                 return;
33768             }
33769             
33770             var positions = [];
33771             
33772             switch (box.length){
33773                 case 1 :
33774                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33775                     break;
33776                 case 2 :
33777                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33778                     break;
33779                 case 3 :
33780                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33781                     break;
33782                 case 4 :
33783                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33784                     break;
33785                 default :
33786                     break;
33787             }
33788             
33789             Roo.each(box, function(b,kk){
33790                 
33791                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33792                 
33793                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33794                 
33795             }, this);
33796             
33797         }, this);
33798         
33799     },
33800     
33801     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33802     {
33803         Roo.each(eItems, function(b,k){
33804             
33805             b.size = (k == 0) ? 'sm' : 'xs';
33806             b.x = (k == 0) ? 2 : 1;
33807             b.y = (k == 0) ? 2 : 1;
33808             
33809             b.el.position('absolute');
33810             
33811             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33812                 
33813             b.el.setWidth(width);
33814             
33815             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33816             
33817             b.el.setHeight(height);
33818             
33819         }, this);
33820
33821         var positions = [];
33822         
33823         positions.push({
33824             x : maxX - this.unitWidth * 2 - this.gutter,
33825             y : minY
33826         });
33827         
33828         positions.push({
33829             x : maxX - this.unitWidth,
33830             y : minY + (this.unitWidth + this.gutter) * 2
33831         });
33832         
33833         positions.push({
33834             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33835             y : minY
33836         });
33837         
33838         Roo.each(eItems, function(b,k){
33839             
33840             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33841
33842         }, this);
33843         
33844     },
33845     
33846     getVerticalOneBoxColPositions : function(x, y, box)
33847     {
33848         var pos = [];
33849         
33850         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33851         
33852         if(box[0].size == 'md-left'){
33853             rand = 0;
33854         }
33855         
33856         if(box[0].size == 'md-right'){
33857             rand = 1;
33858         }
33859         
33860         pos.push({
33861             x : x + (this.unitWidth + this.gutter) * rand,
33862             y : y
33863         });
33864         
33865         return pos;
33866     },
33867     
33868     getVerticalTwoBoxColPositions : function(x, y, box)
33869     {
33870         var pos = [];
33871         
33872         if(box[0].size == 'xs'){
33873             
33874             pos.push({
33875                 x : x,
33876                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33877             });
33878
33879             pos.push({
33880                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33881                 y : y
33882             });
33883             
33884             return pos;
33885             
33886         }
33887         
33888         pos.push({
33889             x : x,
33890             y : y
33891         });
33892
33893         pos.push({
33894             x : x + (this.unitWidth + this.gutter) * 2,
33895             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33896         });
33897         
33898         return pos;
33899         
33900     },
33901     
33902     getVerticalThreeBoxColPositions : function(x, y, box)
33903     {
33904         var pos = [];
33905         
33906         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33907             
33908             pos.push({
33909                 x : x,
33910                 y : y
33911             });
33912
33913             pos.push({
33914                 x : x + (this.unitWidth + this.gutter) * 1,
33915                 y : y
33916             });
33917             
33918             pos.push({
33919                 x : x + (this.unitWidth + this.gutter) * 2,
33920                 y : y
33921             });
33922             
33923             return pos;
33924             
33925         }
33926         
33927         if(box[0].size == 'xs' && box[1].size == 'xs'){
33928             
33929             pos.push({
33930                 x : x,
33931                 y : y
33932             });
33933
33934             pos.push({
33935                 x : x,
33936                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33937             });
33938             
33939             pos.push({
33940                 x : x + (this.unitWidth + this.gutter) * 1,
33941                 y : y
33942             });
33943             
33944             return pos;
33945             
33946         }
33947         
33948         pos.push({
33949             x : x,
33950             y : y
33951         });
33952
33953         pos.push({
33954             x : x + (this.unitWidth + this.gutter) * 2,
33955             y : y
33956         });
33957
33958         pos.push({
33959             x : x + (this.unitWidth + this.gutter) * 2,
33960             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33961         });
33962             
33963         return pos;
33964         
33965     },
33966     
33967     getVerticalFourBoxColPositions : function(x, y, box)
33968     {
33969         var pos = [];
33970         
33971         if(box[0].size == 'xs'){
33972             
33973             pos.push({
33974                 x : x,
33975                 y : y
33976             });
33977
33978             pos.push({
33979                 x : x,
33980                 y : y + (this.unitHeight + this.gutter) * 1
33981             });
33982             
33983             pos.push({
33984                 x : x,
33985                 y : y + (this.unitHeight + this.gutter) * 2
33986             });
33987             
33988             pos.push({
33989                 x : x + (this.unitWidth + this.gutter) * 1,
33990                 y : y
33991             });
33992             
33993             return pos;
33994             
33995         }
33996         
33997         pos.push({
33998             x : x,
33999             y : y
34000         });
34001
34002         pos.push({
34003             x : x + (this.unitWidth + this.gutter) * 2,
34004             y : y
34005         });
34006
34007         pos.push({
34008             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34009             y : y + (this.unitHeight + this.gutter) * 1
34010         });
34011
34012         pos.push({
34013             x : x + (this.unitWidth + this.gutter) * 2,
34014             y : y + (this.unitWidth + this.gutter) * 2
34015         });
34016
34017         return pos;
34018         
34019     },
34020     
34021     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34022     {
34023         var pos = [];
34024         
34025         if(box[0].size == 'md-left'){
34026             pos.push({
34027                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34028                 y : minY
34029             });
34030             
34031             return pos;
34032         }
34033         
34034         if(box[0].size == 'md-right'){
34035             pos.push({
34036                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34037                 y : minY + (this.unitWidth + this.gutter) * 1
34038             });
34039             
34040             return pos;
34041         }
34042         
34043         var rand = Math.floor(Math.random() * (4 - box[0].y));
34044         
34045         pos.push({
34046             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34047             y : minY + (this.unitWidth + this.gutter) * rand
34048         });
34049         
34050         return pos;
34051         
34052     },
34053     
34054     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34055     {
34056         var pos = [];
34057         
34058         if(box[0].size == 'xs'){
34059             
34060             pos.push({
34061                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34062                 y : minY
34063             });
34064
34065             pos.push({
34066                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34067                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34068             });
34069             
34070             return pos;
34071             
34072         }
34073         
34074         pos.push({
34075             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34076             y : minY
34077         });
34078
34079         pos.push({
34080             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34081             y : minY + (this.unitWidth + this.gutter) * 2
34082         });
34083         
34084         return pos;
34085         
34086     },
34087     
34088     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34089     {
34090         var pos = [];
34091         
34092         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34093             
34094             pos.push({
34095                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34096                 y : minY
34097             });
34098
34099             pos.push({
34100                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34101                 y : minY + (this.unitWidth + this.gutter) * 1
34102             });
34103             
34104             pos.push({
34105                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34106                 y : minY + (this.unitWidth + this.gutter) * 2
34107             });
34108             
34109             return pos;
34110             
34111         }
34112         
34113         if(box[0].size == 'xs' && box[1].size == 'xs'){
34114             
34115             pos.push({
34116                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34117                 y : minY
34118             });
34119
34120             pos.push({
34121                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34122                 y : minY
34123             });
34124             
34125             pos.push({
34126                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34127                 y : minY + (this.unitWidth + this.gutter) * 1
34128             });
34129             
34130             return pos;
34131             
34132         }
34133         
34134         pos.push({
34135             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34136             y : minY
34137         });
34138
34139         pos.push({
34140             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34141             y : minY + (this.unitWidth + this.gutter) * 2
34142         });
34143
34144         pos.push({
34145             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34146             y : minY + (this.unitWidth + this.gutter) * 2
34147         });
34148             
34149         return pos;
34150         
34151     },
34152     
34153     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34154     {
34155         var pos = [];
34156         
34157         if(box[0].size == 'xs'){
34158             
34159             pos.push({
34160                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34161                 y : minY
34162             });
34163
34164             pos.push({
34165                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34166                 y : minY
34167             });
34168             
34169             pos.push({
34170                 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),
34171                 y : minY
34172             });
34173             
34174             pos.push({
34175                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34176                 y : minY + (this.unitWidth + this.gutter) * 1
34177             });
34178             
34179             return pos;
34180             
34181         }
34182         
34183         pos.push({
34184             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34185             y : minY
34186         });
34187         
34188         pos.push({
34189             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34190             y : minY + (this.unitWidth + this.gutter) * 2
34191         });
34192         
34193         pos.push({
34194             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34195             y : minY + (this.unitWidth + this.gutter) * 2
34196         });
34197         
34198         pos.push({
34199             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),
34200             y : minY + (this.unitWidth + this.gutter) * 2
34201         });
34202
34203         return pos;
34204         
34205     },
34206     
34207     /**
34208     * remove a Masonry Brick
34209     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34210     */
34211     removeBrick : function(brick_id)
34212     {
34213         if (!brick_id) {
34214             return;
34215         }
34216         
34217         for (var i = 0; i<this.bricks.length; i++) {
34218             if (this.bricks[i].id == brick_id) {
34219                 this.bricks.splice(i,1);
34220                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34221                 this.initial();
34222             }
34223         }
34224     },
34225     
34226     /**
34227     * adds a Masonry Brick
34228     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34229     */
34230     addBrick : function(cfg)
34231     {
34232         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34233         //this.register(cn);
34234         cn.parentId = this.id;
34235         cn.render(this.el);
34236         return cn;
34237     },
34238     
34239     /**
34240     * register a Masonry Brick
34241     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34242     */
34243     
34244     register : function(brick)
34245     {
34246         this.bricks.push(brick);
34247         brick.masonryId = this.id;
34248     },
34249     
34250     /**
34251     * clear all the Masonry Brick
34252     */
34253     clearAll : function()
34254     {
34255         this.bricks = [];
34256         //this.getChildContainer().dom.innerHTML = "";
34257         this.el.dom.innerHTML = '';
34258     },
34259     
34260     getSelected : function()
34261     {
34262         if (!this.selectedBrick) {
34263             return false;
34264         }
34265         
34266         return this.selectedBrick;
34267     }
34268 });
34269
34270 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34271     
34272     groups: {},
34273      /**
34274     * register a Masonry Layout
34275     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34276     */
34277     
34278     register : function(layout)
34279     {
34280         this.groups[layout.id] = layout;
34281     },
34282     /**
34283     * fetch a  Masonry Layout based on the masonry layout ID
34284     * @param {string} the masonry layout to add
34285     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34286     */
34287     
34288     get: function(layout_id) {
34289         if (typeof(this.groups[layout_id]) == 'undefined') {
34290             return false;
34291         }
34292         return this.groups[layout_id] ;
34293     }
34294     
34295     
34296     
34297 });
34298
34299  
34300
34301  /**
34302  *
34303  * This is based on 
34304  * http://masonry.desandro.com
34305  *
34306  * The idea is to render all the bricks based on vertical width...
34307  *
34308  * The original code extends 'outlayer' - we might need to use that....
34309  * 
34310  */
34311
34312
34313 /**
34314  * @class Roo.bootstrap.LayoutMasonryAuto
34315  * @extends Roo.bootstrap.Component
34316  * Bootstrap Layout Masonry class
34317  * 
34318  * @constructor
34319  * Create a new Element
34320  * @param {Object} config The config object
34321  */
34322
34323 Roo.bootstrap.LayoutMasonryAuto = function(config){
34324     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34325 };
34326
34327 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34328     
34329       /**
34330      * @cfg {Boolean} isFitWidth  - resize the width..
34331      */   
34332     isFitWidth : false,  // options..
34333     /**
34334      * @cfg {Boolean} isOriginLeft = left align?
34335      */   
34336     isOriginLeft : true,
34337     /**
34338      * @cfg {Boolean} isOriginTop = top align?
34339      */   
34340     isOriginTop : false,
34341     /**
34342      * @cfg {Boolean} isLayoutInstant = no animation?
34343      */   
34344     isLayoutInstant : false, // needed?
34345     /**
34346      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34347      */   
34348     isResizingContainer : true,
34349     /**
34350      * @cfg {Number} columnWidth  width of the columns 
34351      */   
34352     
34353     columnWidth : 0,
34354     
34355     /**
34356      * @cfg {Number} maxCols maximum number of columns
34357      */   
34358     
34359     maxCols: 0,
34360     /**
34361      * @cfg {Number} padHeight padding below box..
34362      */   
34363     
34364     padHeight : 10, 
34365     
34366     /**
34367      * @cfg {Boolean} isAutoInitial defalut true
34368      */   
34369     
34370     isAutoInitial : true, 
34371     
34372     // private?
34373     gutter : 0,
34374     
34375     containerWidth: 0,
34376     initialColumnWidth : 0,
34377     currentSize : null,
34378     
34379     colYs : null, // array.
34380     maxY : 0,
34381     padWidth: 10,
34382     
34383     
34384     tag: 'div',
34385     cls: '',
34386     bricks: null, //CompositeElement
34387     cols : 0, // array?
34388     // element : null, // wrapped now this.el
34389     _isLayoutInited : null, 
34390     
34391     
34392     getAutoCreate : function(){
34393         
34394         var cfg = {
34395             tag: this.tag,
34396             cls: 'blog-masonary-wrapper ' + this.cls,
34397             cn : {
34398                 cls : 'mas-boxes masonary'
34399             }
34400         };
34401         
34402         return cfg;
34403     },
34404     
34405     getChildContainer: function( )
34406     {
34407         if (this.boxesEl) {
34408             return this.boxesEl;
34409         }
34410         
34411         this.boxesEl = this.el.select('.mas-boxes').first();
34412         
34413         return this.boxesEl;
34414     },
34415     
34416     
34417     initEvents : function()
34418     {
34419         var _this = this;
34420         
34421         if(this.isAutoInitial){
34422             Roo.log('hook children rendered');
34423             this.on('childrenrendered', function() {
34424                 Roo.log('children rendered');
34425                 _this.initial();
34426             } ,this);
34427         }
34428         
34429     },
34430     
34431     initial : function()
34432     {
34433         this.reloadItems();
34434
34435         this.currentSize = this.el.getBox(true);
34436
34437         /// was window resize... - let's see if this works..
34438         Roo.EventManager.onWindowResize(this.resize, this); 
34439
34440         if(!this.isAutoInitial){
34441             this.layout();
34442             return;
34443         }
34444         
34445         this.layout.defer(500,this);
34446     },
34447     
34448     reloadItems: function()
34449     {
34450         this.bricks = this.el.select('.masonry-brick', true);
34451         
34452         this.bricks.each(function(b) {
34453             //Roo.log(b.getSize());
34454             if (!b.attr('originalwidth')) {
34455                 b.attr('originalwidth',  b.getSize().width);
34456             }
34457             
34458         });
34459         
34460         Roo.log(this.bricks.elements.length);
34461     },
34462     
34463     resize : function()
34464     {
34465         Roo.log('resize');
34466         var cs = this.el.getBox(true);
34467         
34468         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34469             Roo.log("no change in with or X");
34470             return;
34471         }
34472         this.currentSize = cs;
34473         this.layout();
34474     },
34475     
34476     layout : function()
34477     {
34478          Roo.log('layout');
34479         this._resetLayout();
34480         //this._manageStamps();
34481       
34482         // don't animate first layout
34483         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34484         this.layoutItems( isInstant );
34485       
34486         // flag for initalized
34487         this._isLayoutInited = true;
34488     },
34489     
34490     layoutItems : function( isInstant )
34491     {
34492         //var items = this._getItemsForLayout( this.items );
34493         // original code supports filtering layout items.. we just ignore it..
34494         
34495         this._layoutItems( this.bricks , isInstant );
34496       
34497         this._postLayout();
34498     },
34499     _layoutItems : function ( items , isInstant)
34500     {
34501        //this.fireEvent( 'layout', this, items );
34502     
34503
34504         if ( !items || !items.elements.length ) {
34505           // no items, emit event with empty array
34506             return;
34507         }
34508
34509         var queue = [];
34510         items.each(function(item) {
34511             Roo.log("layout item");
34512             Roo.log(item);
34513             // get x/y object from method
34514             var position = this._getItemLayoutPosition( item );
34515             // enqueue
34516             position.item = item;
34517             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34518             queue.push( position );
34519         }, this);
34520       
34521         this._processLayoutQueue( queue );
34522     },
34523     /** Sets position of item in DOM
34524     * @param {Element} item
34525     * @param {Number} x - horizontal position
34526     * @param {Number} y - vertical position
34527     * @param {Boolean} isInstant - disables transitions
34528     */
34529     _processLayoutQueue : function( queue )
34530     {
34531         for ( var i=0, len = queue.length; i < len; i++ ) {
34532             var obj = queue[i];
34533             obj.item.position('absolute');
34534             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34535         }
34536     },
34537       
34538     
34539     /**
34540     * Any logic you want to do after each layout,
34541     * i.e. size the container
34542     */
34543     _postLayout : function()
34544     {
34545         this.resizeContainer();
34546     },
34547     
34548     resizeContainer : function()
34549     {
34550         if ( !this.isResizingContainer ) {
34551             return;
34552         }
34553         var size = this._getContainerSize();
34554         if ( size ) {
34555             this.el.setSize(size.width,size.height);
34556             this.boxesEl.setSize(size.width,size.height);
34557         }
34558     },
34559     
34560     
34561     
34562     _resetLayout : function()
34563     {
34564         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34565         this.colWidth = this.el.getWidth();
34566         //this.gutter = this.el.getWidth(); 
34567         
34568         this.measureColumns();
34569
34570         // reset column Y
34571         var i = this.cols;
34572         this.colYs = [];
34573         while (i--) {
34574             this.colYs.push( 0 );
34575         }
34576     
34577         this.maxY = 0;
34578     },
34579
34580     measureColumns : function()
34581     {
34582         this.getContainerWidth();
34583       // if columnWidth is 0, default to outerWidth of first item
34584         if ( !this.columnWidth ) {
34585             var firstItem = this.bricks.first();
34586             Roo.log(firstItem);
34587             this.columnWidth  = this.containerWidth;
34588             if (firstItem && firstItem.attr('originalwidth') ) {
34589                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34590             }
34591             // columnWidth fall back to item of first element
34592             Roo.log("set column width?");
34593                         this.initialColumnWidth = this.columnWidth  ;
34594
34595             // if first elem has no width, default to size of container
34596             
34597         }
34598         
34599         
34600         if (this.initialColumnWidth) {
34601             this.columnWidth = this.initialColumnWidth;
34602         }
34603         
34604         
34605             
34606         // column width is fixed at the top - however if container width get's smaller we should
34607         // reduce it...
34608         
34609         // this bit calcs how man columns..
34610             
34611         var columnWidth = this.columnWidth += this.gutter;
34612       
34613         // calculate columns
34614         var containerWidth = this.containerWidth + this.gutter;
34615         
34616         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34617         // fix rounding errors, typically with gutters
34618         var excess = columnWidth - containerWidth % columnWidth;
34619         
34620         
34621         // if overshoot is less than a pixel, round up, otherwise floor it
34622         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34623         cols = Math[ mathMethod ]( cols );
34624         this.cols = Math.max( cols, 1 );
34625         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34626         
34627          // padding positioning..
34628         var totalColWidth = this.cols * this.columnWidth;
34629         var padavail = this.containerWidth - totalColWidth;
34630         // so for 2 columns - we need 3 'pads'
34631         
34632         var padNeeded = (1+this.cols) * this.padWidth;
34633         
34634         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34635         
34636         this.columnWidth += padExtra
34637         //this.padWidth = Math.floor(padavail /  ( this.cols));
34638         
34639         // adjust colum width so that padding is fixed??
34640         
34641         // we have 3 columns ... total = width * 3
34642         // we have X left over... that should be used by 
34643         
34644         //if (this.expandC) {
34645             
34646         //}
34647         
34648         
34649         
34650     },
34651     
34652     getContainerWidth : function()
34653     {
34654        /* // container is parent if fit width
34655         var container = this.isFitWidth ? this.element.parentNode : this.element;
34656         // check that this.size and size are there
34657         // IE8 triggers resize on body size change, so they might not be
34658         
34659         var size = getSize( container );  //FIXME
34660         this.containerWidth = size && size.innerWidth; //FIXME
34661         */
34662          
34663         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34664         
34665     },
34666     
34667     _getItemLayoutPosition : function( item )  // what is item?
34668     {
34669         // we resize the item to our columnWidth..
34670       
34671         item.setWidth(this.columnWidth);
34672         item.autoBoxAdjust  = false;
34673         
34674         var sz = item.getSize();
34675  
34676         // how many columns does this brick span
34677         var remainder = this.containerWidth % this.columnWidth;
34678         
34679         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34680         // round if off by 1 pixel, otherwise use ceil
34681         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34682         colSpan = Math.min( colSpan, this.cols );
34683         
34684         // normally this should be '1' as we dont' currently allow multi width columns..
34685         
34686         var colGroup = this._getColGroup( colSpan );
34687         // get the minimum Y value from the columns
34688         var minimumY = Math.min.apply( Math, colGroup );
34689         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34690         
34691         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34692          
34693         // position the brick
34694         var position = {
34695             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34696             y: this.currentSize.y + minimumY + this.padHeight
34697         };
34698         
34699         Roo.log(position);
34700         // apply setHeight to necessary columns
34701         var setHeight = minimumY + sz.height + this.padHeight;
34702         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34703         
34704         var setSpan = this.cols + 1 - colGroup.length;
34705         for ( var i = 0; i < setSpan; i++ ) {
34706           this.colYs[ shortColIndex + i ] = setHeight ;
34707         }
34708       
34709         return position;
34710     },
34711     
34712     /**
34713      * @param {Number} colSpan - number of columns the element spans
34714      * @returns {Array} colGroup
34715      */
34716     _getColGroup : function( colSpan )
34717     {
34718         if ( colSpan < 2 ) {
34719           // if brick spans only one column, use all the column Ys
34720           return this.colYs;
34721         }
34722       
34723         var colGroup = [];
34724         // how many different places could this brick fit horizontally
34725         var groupCount = this.cols + 1 - colSpan;
34726         // for each group potential horizontal position
34727         for ( var i = 0; i < groupCount; i++ ) {
34728           // make an array of colY values for that one group
34729           var groupColYs = this.colYs.slice( i, i + colSpan );
34730           // and get the max value of the array
34731           colGroup[i] = Math.max.apply( Math, groupColYs );
34732         }
34733         return colGroup;
34734     },
34735     /*
34736     _manageStamp : function( stamp )
34737     {
34738         var stampSize =  stamp.getSize();
34739         var offset = stamp.getBox();
34740         // get the columns that this stamp affects
34741         var firstX = this.isOriginLeft ? offset.x : offset.right;
34742         var lastX = firstX + stampSize.width;
34743         var firstCol = Math.floor( firstX / this.columnWidth );
34744         firstCol = Math.max( 0, firstCol );
34745         
34746         var lastCol = Math.floor( lastX / this.columnWidth );
34747         // lastCol should not go over if multiple of columnWidth #425
34748         lastCol -= lastX % this.columnWidth ? 0 : 1;
34749         lastCol = Math.min( this.cols - 1, lastCol );
34750         
34751         // set colYs to bottom of the stamp
34752         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34753             stampSize.height;
34754             
34755         for ( var i = firstCol; i <= lastCol; i++ ) {
34756           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34757         }
34758     },
34759     */
34760     
34761     _getContainerSize : function()
34762     {
34763         this.maxY = Math.max.apply( Math, this.colYs );
34764         var size = {
34765             height: this.maxY
34766         };
34767       
34768         if ( this.isFitWidth ) {
34769             size.width = this._getContainerFitWidth();
34770         }
34771       
34772         return size;
34773     },
34774     
34775     _getContainerFitWidth : function()
34776     {
34777         var unusedCols = 0;
34778         // count unused columns
34779         var i = this.cols;
34780         while ( --i ) {
34781           if ( this.colYs[i] !== 0 ) {
34782             break;
34783           }
34784           unusedCols++;
34785         }
34786         // fit container to columns that have been used
34787         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34788     },
34789     
34790     needsResizeLayout : function()
34791     {
34792         var previousWidth = this.containerWidth;
34793         this.getContainerWidth();
34794         return previousWidth !== this.containerWidth;
34795     }
34796  
34797 });
34798
34799  
34800
34801  /*
34802  * - LGPL
34803  *
34804  * element
34805  * 
34806  */
34807
34808 /**
34809  * @class Roo.bootstrap.MasonryBrick
34810  * @extends Roo.bootstrap.Component
34811  * Bootstrap MasonryBrick class
34812  * 
34813  * @constructor
34814  * Create a new MasonryBrick
34815  * @param {Object} config The config object
34816  */
34817
34818 Roo.bootstrap.MasonryBrick = function(config){
34819     
34820     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34821     
34822     Roo.bootstrap.MasonryBrick.register(this);
34823     
34824     this.addEvents({
34825         // raw events
34826         /**
34827          * @event click
34828          * When a MasonryBrick is clcik
34829          * @param {Roo.bootstrap.MasonryBrick} this
34830          * @param {Roo.EventObject} e
34831          */
34832         "click" : true
34833     });
34834 };
34835
34836 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34837     
34838     /**
34839      * @cfg {String} title
34840      */   
34841     title : '',
34842     /**
34843      * @cfg {String} html
34844      */   
34845     html : '',
34846     /**
34847      * @cfg {String} bgimage
34848      */   
34849     bgimage : '',
34850     /**
34851      * @cfg {String} videourl
34852      */   
34853     videourl : '',
34854     /**
34855      * @cfg {String} cls
34856      */   
34857     cls : '',
34858     /**
34859      * @cfg {String} href
34860      */   
34861     href : '',
34862     /**
34863      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34864      */   
34865     size : 'xs',
34866     
34867     /**
34868      * @cfg {String} placetitle (center|bottom)
34869      */   
34870     placetitle : '',
34871     
34872     /**
34873      * @cfg {Boolean} isFitContainer defalut true
34874      */   
34875     isFitContainer : true, 
34876     
34877     /**
34878      * @cfg {Boolean} preventDefault defalut false
34879      */   
34880     preventDefault : false, 
34881     
34882     /**
34883      * @cfg {Boolean} inverse defalut false
34884      */   
34885     maskInverse : false, 
34886     
34887     getAutoCreate : function()
34888     {
34889         if(!this.isFitContainer){
34890             return this.getSplitAutoCreate();
34891         }
34892         
34893         var cls = 'masonry-brick masonry-brick-full';
34894         
34895         if(this.href.length){
34896             cls += ' masonry-brick-link';
34897         }
34898         
34899         if(this.bgimage.length){
34900             cls += ' masonry-brick-image';
34901         }
34902         
34903         if(this.maskInverse){
34904             cls += ' mask-inverse';
34905         }
34906         
34907         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34908             cls += ' enable-mask';
34909         }
34910         
34911         if(this.size){
34912             cls += ' masonry-' + this.size + '-brick';
34913         }
34914         
34915         if(this.placetitle.length){
34916             
34917             switch (this.placetitle) {
34918                 case 'center' :
34919                     cls += ' masonry-center-title';
34920                     break;
34921                 case 'bottom' :
34922                     cls += ' masonry-bottom-title';
34923                     break;
34924                 default:
34925                     break;
34926             }
34927             
34928         } else {
34929             if(!this.html.length && !this.bgimage.length){
34930                 cls += ' masonry-center-title';
34931             }
34932
34933             if(!this.html.length && this.bgimage.length){
34934                 cls += ' masonry-bottom-title';
34935             }
34936         }
34937         
34938         if(this.cls){
34939             cls += ' ' + this.cls;
34940         }
34941         
34942         var cfg = {
34943             tag: (this.href.length) ? 'a' : 'div',
34944             cls: cls,
34945             cn: [
34946                 {
34947                     tag: 'div',
34948                     cls: 'masonry-brick-mask'
34949                 },
34950                 {
34951                     tag: 'div',
34952                     cls: 'masonry-brick-paragraph',
34953                     cn: []
34954                 }
34955             ]
34956         };
34957         
34958         if(this.href.length){
34959             cfg.href = this.href;
34960         }
34961         
34962         var cn = cfg.cn[1].cn;
34963         
34964         if(this.title.length){
34965             cn.push({
34966                 tag: 'h4',
34967                 cls: 'masonry-brick-title',
34968                 html: this.title
34969             });
34970         }
34971         
34972         if(this.html.length){
34973             cn.push({
34974                 tag: 'p',
34975                 cls: 'masonry-brick-text',
34976                 html: this.html
34977             });
34978         }
34979         
34980         if (!this.title.length && !this.html.length) {
34981             cfg.cn[1].cls += ' hide';
34982         }
34983         
34984         if(this.bgimage.length){
34985             cfg.cn.push({
34986                 tag: 'img',
34987                 cls: 'masonry-brick-image-view',
34988                 src: this.bgimage
34989             });
34990         }
34991         
34992         if(this.videourl.length){
34993             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34994             // youtube support only?
34995             cfg.cn.push({
34996                 tag: 'iframe',
34997                 cls: 'masonry-brick-image-view',
34998                 src: vurl,
34999                 frameborder : 0,
35000                 allowfullscreen : true
35001             });
35002         }
35003         
35004         return cfg;
35005         
35006     },
35007     
35008     getSplitAutoCreate : function()
35009     {
35010         var cls = 'masonry-brick masonry-brick-split';
35011         
35012         if(this.href.length){
35013             cls += ' masonry-brick-link';
35014         }
35015         
35016         if(this.bgimage.length){
35017             cls += ' masonry-brick-image';
35018         }
35019         
35020         if(this.size){
35021             cls += ' masonry-' + this.size + '-brick';
35022         }
35023         
35024         switch (this.placetitle) {
35025             case 'center' :
35026                 cls += ' masonry-center-title';
35027                 break;
35028             case 'bottom' :
35029                 cls += ' masonry-bottom-title';
35030                 break;
35031             default:
35032                 if(!this.bgimage.length){
35033                     cls += ' masonry-center-title';
35034                 }
35035
35036                 if(this.bgimage.length){
35037                     cls += ' masonry-bottom-title';
35038                 }
35039                 break;
35040         }
35041         
35042         if(this.cls){
35043             cls += ' ' + this.cls;
35044         }
35045         
35046         var cfg = {
35047             tag: (this.href.length) ? 'a' : 'div',
35048             cls: cls,
35049             cn: [
35050                 {
35051                     tag: 'div',
35052                     cls: 'masonry-brick-split-head',
35053                     cn: [
35054                         {
35055                             tag: 'div',
35056                             cls: 'masonry-brick-paragraph',
35057                             cn: []
35058                         }
35059                     ]
35060                 },
35061                 {
35062                     tag: 'div',
35063                     cls: 'masonry-brick-split-body',
35064                     cn: []
35065                 }
35066             ]
35067         };
35068         
35069         if(this.href.length){
35070             cfg.href = this.href;
35071         }
35072         
35073         if(this.title.length){
35074             cfg.cn[0].cn[0].cn.push({
35075                 tag: 'h4',
35076                 cls: 'masonry-brick-title',
35077                 html: this.title
35078             });
35079         }
35080         
35081         if(this.html.length){
35082             cfg.cn[1].cn.push({
35083                 tag: 'p',
35084                 cls: 'masonry-brick-text',
35085                 html: this.html
35086             });
35087         }
35088
35089         if(this.bgimage.length){
35090             cfg.cn[0].cn.push({
35091                 tag: 'img',
35092                 cls: 'masonry-brick-image-view',
35093                 src: this.bgimage
35094             });
35095         }
35096         
35097         if(this.videourl.length){
35098             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35099             // youtube support only?
35100             cfg.cn[0].cn.cn.push({
35101                 tag: 'iframe',
35102                 cls: 'masonry-brick-image-view',
35103                 src: vurl,
35104                 frameborder : 0,
35105                 allowfullscreen : true
35106             });
35107         }
35108         
35109         return cfg;
35110     },
35111     
35112     initEvents: function() 
35113     {
35114         switch (this.size) {
35115             case 'xs' :
35116                 this.x = 1;
35117                 this.y = 1;
35118                 break;
35119             case 'sm' :
35120                 this.x = 2;
35121                 this.y = 2;
35122                 break;
35123             case 'md' :
35124             case 'md-left' :
35125             case 'md-right' :
35126                 this.x = 3;
35127                 this.y = 3;
35128                 break;
35129             case 'tall' :
35130                 this.x = 2;
35131                 this.y = 3;
35132                 break;
35133             case 'wide' :
35134                 this.x = 3;
35135                 this.y = 2;
35136                 break;
35137             case 'wide-thin' :
35138                 this.x = 3;
35139                 this.y = 1;
35140                 break;
35141                         
35142             default :
35143                 break;
35144         }
35145         
35146         if(Roo.isTouch){
35147             this.el.on('touchstart', this.onTouchStart, this);
35148             this.el.on('touchmove', this.onTouchMove, this);
35149             this.el.on('touchend', this.onTouchEnd, this);
35150             this.el.on('contextmenu', this.onContextMenu, this);
35151         } else {
35152             this.el.on('mouseenter'  ,this.enter, this);
35153             this.el.on('mouseleave', this.leave, this);
35154             this.el.on('click', this.onClick, this);
35155         }
35156         
35157         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35158             this.parent().bricks.push(this);   
35159         }
35160         
35161     },
35162     
35163     onClick: function(e, el)
35164     {
35165         var time = this.endTimer - this.startTimer;
35166         // Roo.log(e.preventDefault());
35167         if(Roo.isTouch){
35168             if(time > 1000){
35169                 e.preventDefault();
35170                 return;
35171             }
35172         }
35173         
35174         if(!this.preventDefault){
35175             return;
35176         }
35177         
35178         e.preventDefault();
35179         
35180         if (this.activeClass != '') {
35181             this.selectBrick();
35182         }
35183         
35184         this.fireEvent('click', this, e);
35185     },
35186     
35187     enter: function(e, el)
35188     {
35189         e.preventDefault();
35190         
35191         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35192             return;
35193         }
35194         
35195         if(this.bgimage.length && this.html.length){
35196             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35197         }
35198     },
35199     
35200     leave: function(e, el)
35201     {
35202         e.preventDefault();
35203         
35204         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35205             return;
35206         }
35207         
35208         if(this.bgimage.length && this.html.length){
35209             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35210         }
35211     },
35212     
35213     onTouchStart: function(e, el)
35214     {
35215 //        e.preventDefault();
35216         
35217         this.touchmoved = false;
35218         
35219         if(!this.isFitContainer){
35220             return;
35221         }
35222         
35223         if(!this.bgimage.length || !this.html.length){
35224             return;
35225         }
35226         
35227         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35228         
35229         this.timer = new Date().getTime();
35230         
35231     },
35232     
35233     onTouchMove: function(e, el)
35234     {
35235         this.touchmoved = true;
35236     },
35237     
35238     onContextMenu : function(e,el)
35239     {
35240         e.preventDefault();
35241         e.stopPropagation();
35242         return false;
35243     },
35244     
35245     onTouchEnd: function(e, el)
35246     {
35247 //        e.preventDefault();
35248         
35249         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35250         
35251             this.leave(e,el);
35252             
35253             return;
35254         }
35255         
35256         if(!this.bgimage.length || !this.html.length){
35257             
35258             if(this.href.length){
35259                 window.location.href = this.href;
35260             }
35261             
35262             return;
35263         }
35264         
35265         if(!this.isFitContainer){
35266             return;
35267         }
35268         
35269         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35270         
35271         window.location.href = this.href;
35272     },
35273     
35274     //selection on single brick only
35275     selectBrick : function() {
35276         
35277         if (!this.parentId) {
35278             return;
35279         }
35280         
35281         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35282         var index = m.selectedBrick.indexOf(this.id);
35283         
35284         if ( index > -1) {
35285             m.selectedBrick.splice(index,1);
35286             this.el.removeClass(this.activeClass);
35287             return;
35288         }
35289         
35290         for(var i = 0; i < m.selectedBrick.length; i++) {
35291             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35292             b.el.removeClass(b.activeClass);
35293         }
35294         
35295         m.selectedBrick = [];
35296         
35297         m.selectedBrick.push(this.id);
35298         this.el.addClass(this.activeClass);
35299         return;
35300     },
35301     
35302     isSelected : function(){
35303         return this.el.hasClass(this.activeClass);
35304         
35305     }
35306 });
35307
35308 Roo.apply(Roo.bootstrap.MasonryBrick, {
35309     
35310     //groups: {},
35311     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35312      /**
35313     * register a Masonry Brick
35314     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35315     */
35316     
35317     register : function(brick)
35318     {
35319         //this.groups[brick.id] = brick;
35320         this.groups.add(brick.id, brick);
35321     },
35322     /**
35323     * fetch a  masonry brick based on the masonry brick ID
35324     * @param {string} the masonry brick to add
35325     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35326     */
35327     
35328     get: function(brick_id) 
35329     {
35330         // if (typeof(this.groups[brick_id]) == 'undefined') {
35331         //     return false;
35332         // }
35333         // return this.groups[brick_id] ;
35334         
35335         if(this.groups.key(brick_id)) {
35336             return this.groups.key(brick_id);
35337         }
35338         
35339         return false;
35340     }
35341     
35342     
35343     
35344 });
35345
35346  /*
35347  * - LGPL
35348  *
35349  * element
35350  * 
35351  */
35352
35353 /**
35354  * @class Roo.bootstrap.Brick
35355  * @extends Roo.bootstrap.Component
35356  * Bootstrap Brick class
35357  * 
35358  * @constructor
35359  * Create a new Brick
35360  * @param {Object} config The config object
35361  */
35362
35363 Roo.bootstrap.Brick = function(config){
35364     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35365     
35366     this.addEvents({
35367         // raw events
35368         /**
35369          * @event click
35370          * When a Brick is click
35371          * @param {Roo.bootstrap.Brick} this
35372          * @param {Roo.EventObject} e
35373          */
35374         "click" : true
35375     });
35376 };
35377
35378 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35379     
35380     /**
35381      * @cfg {String} title
35382      */   
35383     title : '',
35384     /**
35385      * @cfg {String} html
35386      */   
35387     html : '',
35388     /**
35389      * @cfg {String} bgimage
35390      */   
35391     bgimage : '',
35392     /**
35393      * @cfg {String} cls
35394      */   
35395     cls : '',
35396     /**
35397      * @cfg {String} href
35398      */   
35399     href : '',
35400     /**
35401      * @cfg {String} video
35402      */   
35403     video : '',
35404     /**
35405      * @cfg {Boolean} square
35406      */   
35407     square : true,
35408     
35409     getAutoCreate : function()
35410     {
35411         var cls = 'roo-brick';
35412         
35413         if(this.href.length){
35414             cls += ' roo-brick-link';
35415         }
35416         
35417         if(this.bgimage.length){
35418             cls += ' roo-brick-image';
35419         }
35420         
35421         if(!this.html.length && !this.bgimage.length){
35422             cls += ' roo-brick-center-title';
35423         }
35424         
35425         if(!this.html.length && this.bgimage.length){
35426             cls += ' roo-brick-bottom-title';
35427         }
35428         
35429         if(this.cls){
35430             cls += ' ' + this.cls;
35431         }
35432         
35433         var cfg = {
35434             tag: (this.href.length) ? 'a' : 'div',
35435             cls: cls,
35436             cn: [
35437                 {
35438                     tag: 'div',
35439                     cls: 'roo-brick-paragraph',
35440                     cn: []
35441                 }
35442             ]
35443         };
35444         
35445         if(this.href.length){
35446             cfg.href = this.href;
35447         }
35448         
35449         var cn = cfg.cn[0].cn;
35450         
35451         if(this.title.length){
35452             cn.push({
35453                 tag: 'h4',
35454                 cls: 'roo-brick-title',
35455                 html: this.title
35456             });
35457         }
35458         
35459         if(this.html.length){
35460             cn.push({
35461                 tag: 'p',
35462                 cls: 'roo-brick-text',
35463                 html: this.html
35464             });
35465         } else {
35466             cn.cls += ' hide';
35467         }
35468         
35469         if(this.bgimage.length){
35470             cfg.cn.push({
35471                 tag: 'img',
35472                 cls: 'roo-brick-image-view',
35473                 src: this.bgimage
35474             });
35475         }
35476         
35477         return cfg;
35478     },
35479     
35480     initEvents: function() 
35481     {
35482         if(this.title.length || this.html.length){
35483             this.el.on('mouseenter'  ,this.enter, this);
35484             this.el.on('mouseleave', this.leave, this);
35485         }
35486         
35487         Roo.EventManager.onWindowResize(this.resize, this); 
35488         
35489         if(this.bgimage.length){
35490             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35491             this.imageEl.on('load', this.onImageLoad, this);
35492             return;
35493         }
35494         
35495         this.resize();
35496     },
35497     
35498     onImageLoad : function()
35499     {
35500         this.resize();
35501     },
35502     
35503     resize : function()
35504     {
35505         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35506         
35507         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35508         
35509         if(this.bgimage.length){
35510             var image = this.el.select('.roo-brick-image-view', true).first();
35511             
35512             image.setWidth(paragraph.getWidth());
35513             
35514             if(this.square){
35515                 image.setHeight(paragraph.getWidth());
35516             }
35517             
35518             this.el.setHeight(image.getHeight());
35519             paragraph.setHeight(image.getHeight());
35520             
35521         }
35522         
35523     },
35524     
35525     enter: function(e, el)
35526     {
35527         e.preventDefault();
35528         
35529         if(this.bgimage.length){
35530             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35531             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35532         }
35533     },
35534     
35535     leave: function(e, el)
35536     {
35537         e.preventDefault();
35538         
35539         if(this.bgimage.length){
35540             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35541             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35542         }
35543     }
35544     
35545 });
35546
35547  
35548
35549  /*
35550  * - LGPL
35551  *
35552  * Number field 
35553  */
35554
35555 /**
35556  * @class Roo.bootstrap.NumberField
35557  * @extends Roo.bootstrap.Input
35558  * Bootstrap NumberField class
35559  * 
35560  * 
35561  * 
35562  * 
35563  * @constructor
35564  * Create a new NumberField
35565  * @param {Object} config The config object
35566  */
35567
35568 Roo.bootstrap.NumberField = function(config){
35569     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35570 };
35571
35572 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35573     
35574     /**
35575      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35576      */
35577     allowDecimals : true,
35578     /**
35579      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35580      */
35581     decimalSeparator : ".",
35582     /**
35583      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35584      */
35585     decimalPrecision : 2,
35586     /**
35587      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35588      */
35589     allowNegative : true,
35590     
35591     /**
35592      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35593      */
35594     allowZero: true,
35595     /**
35596      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35597      */
35598     minValue : Number.NEGATIVE_INFINITY,
35599     /**
35600      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35601      */
35602     maxValue : Number.MAX_VALUE,
35603     /**
35604      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35605      */
35606     minText : "The minimum value for this field is {0}",
35607     /**
35608      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35609      */
35610     maxText : "The maximum value for this field is {0}",
35611     /**
35612      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35613      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35614      */
35615     nanText : "{0} is not a valid number",
35616     /**
35617      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35618      */
35619     thousandsDelimiter : false,
35620     /**
35621      * @cfg {String} valueAlign alignment of value
35622      */
35623     valueAlign : "left",
35624
35625     getAutoCreate : function()
35626     {
35627         var hiddenInput = {
35628             tag: 'input',
35629             type: 'hidden',
35630             id: Roo.id(),
35631             cls: 'hidden-number-input'
35632         };
35633         
35634         if (this.name) {
35635             hiddenInput.name = this.name;
35636         }
35637         
35638         this.name = '';
35639         
35640         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35641         
35642         this.name = hiddenInput.name;
35643         
35644         if(cfg.cn.length > 0) {
35645             cfg.cn.push(hiddenInput);
35646         }
35647         
35648         return cfg;
35649     },
35650
35651     // private
35652     initEvents : function()
35653     {   
35654         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35655         
35656         var allowed = "0123456789";
35657         
35658         if(this.allowDecimals){
35659             allowed += this.decimalSeparator;
35660         }
35661         
35662         if(this.allowNegative){
35663             allowed += "-";
35664         }
35665         
35666         if(this.thousandsDelimiter) {
35667             allowed += ",";
35668         }
35669         
35670         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35671         
35672         var keyPress = function(e){
35673             
35674             var k = e.getKey();
35675             
35676             var c = e.getCharCode();
35677             
35678             if(
35679                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35680                     allowed.indexOf(String.fromCharCode(c)) === -1
35681             ){
35682                 e.stopEvent();
35683                 return;
35684             }
35685             
35686             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35687                 return;
35688             }
35689             
35690             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35691                 e.stopEvent();
35692             }
35693         };
35694         
35695         this.el.on("keypress", keyPress, this);
35696     },
35697     
35698     validateValue : function(value)
35699     {
35700         
35701         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35702             return false;
35703         }
35704         
35705         var num = this.parseValue(value);
35706         
35707         if(isNaN(num)){
35708             this.markInvalid(String.format(this.nanText, value));
35709             return false;
35710         }
35711         
35712         if(num < this.minValue){
35713             this.markInvalid(String.format(this.minText, this.minValue));
35714             return false;
35715         }
35716         
35717         if(num > this.maxValue){
35718             this.markInvalid(String.format(this.maxText, this.maxValue));
35719             return false;
35720         }
35721         
35722         return true;
35723     },
35724
35725     getValue : function()
35726     {
35727         var v = this.hiddenEl().getValue();
35728         
35729         return this.fixPrecision(this.parseValue(v));
35730     },
35731
35732     parseValue : function(value)
35733     {
35734         if(this.thousandsDelimiter) {
35735             value += "";
35736             r = new RegExp(",", "g");
35737             value = value.replace(r, "");
35738         }
35739         
35740         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35741         return isNaN(value) ? '' : value;
35742     },
35743
35744     fixPrecision : function(value)
35745     {
35746         if(this.thousandsDelimiter) {
35747             value += "";
35748             r = new RegExp(",", "g");
35749             value = value.replace(r, "");
35750         }
35751         
35752         var nan = isNaN(value);
35753         
35754         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35755             return nan ? '' : value;
35756         }
35757         return parseFloat(value).toFixed(this.decimalPrecision);
35758     },
35759
35760     setValue : function(v)
35761     {
35762         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35763         
35764         this.value = v;
35765         
35766         if(this.rendered){
35767             
35768             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35769             
35770             this.inputEl().dom.value = (v == '') ? '' :
35771                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35772             
35773             if(!this.allowZero && v === '0') {
35774                 this.hiddenEl().dom.value = '';
35775                 this.inputEl().dom.value = '';
35776             }
35777             
35778             this.validate();
35779         }
35780     },
35781
35782     decimalPrecisionFcn : function(v)
35783     {
35784         return Math.floor(v);
35785     },
35786
35787     beforeBlur : function()
35788     {
35789         var v = this.parseValue(this.getRawValue());
35790         
35791         if(v || v === 0 || v === ''){
35792             this.setValue(v);
35793         }
35794     },
35795     
35796     hiddenEl : function()
35797     {
35798         return this.el.select('input.hidden-number-input',true).first();
35799     }
35800     
35801 });
35802
35803  
35804
35805 /*
35806 * Licence: LGPL
35807 */
35808
35809 /**
35810  * @class Roo.bootstrap.DocumentSlider
35811  * @extends Roo.bootstrap.Component
35812  * Bootstrap DocumentSlider class
35813  * 
35814  * @constructor
35815  * Create a new DocumentViewer
35816  * @param {Object} config The config object
35817  */
35818
35819 Roo.bootstrap.DocumentSlider = function(config){
35820     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35821     
35822     this.files = [];
35823     
35824     this.addEvents({
35825         /**
35826          * @event initial
35827          * Fire after initEvent
35828          * @param {Roo.bootstrap.DocumentSlider} this
35829          */
35830         "initial" : true,
35831         /**
35832          * @event update
35833          * Fire after update
35834          * @param {Roo.bootstrap.DocumentSlider} this
35835          */
35836         "update" : true,
35837         /**
35838          * @event click
35839          * Fire after click
35840          * @param {Roo.bootstrap.DocumentSlider} this
35841          */
35842         "click" : true
35843     });
35844 };
35845
35846 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35847     
35848     files : false,
35849     
35850     indicator : 0,
35851     
35852     getAutoCreate : function()
35853     {
35854         var cfg = {
35855             tag : 'div',
35856             cls : 'roo-document-slider',
35857             cn : [
35858                 {
35859                     tag : 'div',
35860                     cls : 'roo-document-slider-header',
35861                     cn : [
35862                         {
35863                             tag : 'div',
35864                             cls : 'roo-document-slider-header-title'
35865                         }
35866                     ]
35867                 },
35868                 {
35869                     tag : 'div',
35870                     cls : 'roo-document-slider-body',
35871                     cn : [
35872                         {
35873                             tag : 'div',
35874                             cls : 'roo-document-slider-prev',
35875                             cn : [
35876                                 {
35877                                     tag : 'i',
35878                                     cls : 'fa fa-chevron-left'
35879                                 }
35880                             ]
35881                         },
35882                         {
35883                             tag : 'div',
35884                             cls : 'roo-document-slider-thumb',
35885                             cn : [
35886                                 {
35887                                     tag : 'img',
35888                                     cls : 'roo-document-slider-image'
35889                                 }
35890                             ]
35891                         },
35892                         {
35893                             tag : 'div',
35894                             cls : 'roo-document-slider-next',
35895                             cn : [
35896                                 {
35897                                     tag : 'i',
35898                                     cls : 'fa fa-chevron-right'
35899                                 }
35900                             ]
35901                         }
35902                     ]
35903                 }
35904             ]
35905         };
35906         
35907         return cfg;
35908     },
35909     
35910     initEvents : function()
35911     {
35912         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35913         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35914         
35915         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35916         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35917         
35918         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35919         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35920         
35921         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35922         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35923         
35924         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35925         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35926         
35927         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35928         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35929         
35930         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35931         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35932         
35933         this.thumbEl.on('click', this.onClick, this);
35934         
35935         this.prevIndicator.on('click', this.prev, this);
35936         
35937         this.nextIndicator.on('click', this.next, this);
35938         
35939     },
35940     
35941     initial : function()
35942     {
35943         if(this.files.length){
35944             this.indicator = 1;
35945             this.update()
35946         }
35947         
35948         this.fireEvent('initial', this);
35949     },
35950     
35951     update : function()
35952     {
35953         this.imageEl.attr('src', this.files[this.indicator - 1]);
35954         
35955         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35956         
35957         this.prevIndicator.show();
35958         
35959         if(this.indicator == 1){
35960             this.prevIndicator.hide();
35961         }
35962         
35963         this.nextIndicator.show();
35964         
35965         if(this.indicator == this.files.length){
35966             this.nextIndicator.hide();
35967         }
35968         
35969         this.thumbEl.scrollTo('top');
35970         
35971         this.fireEvent('update', this);
35972     },
35973     
35974     onClick : function(e)
35975     {
35976         e.preventDefault();
35977         
35978         this.fireEvent('click', this);
35979     },
35980     
35981     prev : function(e)
35982     {
35983         e.preventDefault();
35984         
35985         this.indicator = Math.max(1, this.indicator - 1);
35986         
35987         this.update();
35988     },
35989     
35990     next : function(e)
35991     {
35992         e.preventDefault();
35993         
35994         this.indicator = Math.min(this.files.length, this.indicator + 1);
35995         
35996         this.update();
35997     }
35998 });
35999 /*
36000  * - LGPL
36001  *
36002  * RadioSet
36003  *
36004  *
36005  */
36006
36007 /**
36008  * @class Roo.bootstrap.RadioSet
36009  * @extends Roo.bootstrap.Input
36010  * Bootstrap RadioSet class
36011  * @cfg {String} indicatorpos (left|right) default left
36012  * @cfg {Boolean} inline (true|false) inline the element (default true)
36013  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36014  * @constructor
36015  * Create a new RadioSet
36016  * @param {Object} config The config object
36017  */
36018
36019 Roo.bootstrap.RadioSet = function(config){
36020     
36021     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36022     
36023     this.radioes = [];
36024     
36025     Roo.bootstrap.RadioSet.register(this);
36026     
36027     this.addEvents({
36028         /**
36029         * @event check
36030         * Fires when the element is checked or unchecked.
36031         * @param {Roo.bootstrap.RadioSet} this This radio
36032         * @param {Roo.bootstrap.Radio} item The checked item
36033         */
36034        check : true,
36035        /**
36036         * @event click
36037         * Fires when the element is click.
36038         * @param {Roo.bootstrap.RadioSet} this This radio set
36039         * @param {Roo.bootstrap.Radio} item The checked item
36040         * @param {Roo.EventObject} e The event object
36041         */
36042        click : true
36043     });
36044     
36045 };
36046
36047 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36048
36049     radioes : false,
36050     
36051     inline : true,
36052     
36053     weight : '',
36054     
36055     indicatorpos : 'left',
36056     
36057     getAutoCreate : function()
36058     {
36059         var label = {
36060             tag : 'label',
36061             cls : 'roo-radio-set-label',
36062             cn : [
36063                 {
36064                     tag : 'span',
36065                     html : this.fieldLabel
36066                 }
36067             ]
36068         };
36069         if (Roo.bootstrap.version == 3) {
36070             
36071             
36072             if(this.indicatorpos == 'left'){
36073                 label.cn.unshift({
36074                     tag : 'i',
36075                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36076                     tooltip : 'This field is required'
36077                 });
36078             } else {
36079                 label.cn.push({
36080                     tag : 'i',
36081                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36082                     tooltip : 'This field is required'
36083                 });
36084             }
36085         }
36086         var items = {
36087             tag : 'div',
36088             cls : 'roo-radio-set-items'
36089         };
36090         
36091         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36092         
36093         if (align === 'left' && this.fieldLabel.length) {
36094             
36095             items = {
36096                 cls : "roo-radio-set-right", 
36097                 cn: [
36098                     items
36099                 ]
36100             };
36101             
36102             if(this.labelWidth > 12){
36103                 label.style = "width: " + this.labelWidth + 'px';
36104             }
36105             
36106             if(this.labelWidth < 13 && this.labelmd == 0){
36107                 this.labelmd = this.labelWidth;
36108             }
36109             
36110             if(this.labellg > 0){
36111                 label.cls += ' col-lg-' + this.labellg;
36112                 items.cls += ' col-lg-' + (12 - this.labellg);
36113             }
36114             
36115             if(this.labelmd > 0){
36116                 label.cls += ' col-md-' + this.labelmd;
36117                 items.cls += ' col-md-' + (12 - this.labelmd);
36118             }
36119             
36120             if(this.labelsm > 0){
36121                 label.cls += ' col-sm-' + this.labelsm;
36122                 items.cls += ' col-sm-' + (12 - this.labelsm);
36123             }
36124             
36125             if(this.labelxs > 0){
36126                 label.cls += ' col-xs-' + this.labelxs;
36127                 items.cls += ' col-xs-' + (12 - this.labelxs);
36128             }
36129         }
36130         
36131         var cfg = {
36132             tag : 'div',
36133             cls : 'roo-radio-set',
36134             cn : [
36135                 {
36136                     tag : 'input',
36137                     cls : 'roo-radio-set-input',
36138                     type : 'hidden',
36139                     name : this.name,
36140                     value : this.value ? this.value :  ''
36141                 },
36142                 label,
36143                 items
36144             ]
36145         };
36146         
36147         if(this.weight.length){
36148             cfg.cls += ' roo-radio-' + this.weight;
36149         }
36150         
36151         if(this.inline) {
36152             cfg.cls += ' roo-radio-set-inline';
36153         }
36154         
36155         var settings=this;
36156         ['xs','sm','md','lg'].map(function(size){
36157             if (settings[size]) {
36158                 cfg.cls += ' col-' + size + '-' + settings[size];
36159             }
36160         });
36161         
36162         return cfg;
36163         
36164     },
36165
36166     initEvents : function()
36167     {
36168         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36169         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36170         
36171         if(!this.fieldLabel.length){
36172             this.labelEl.hide();
36173         }
36174         
36175         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36176         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36177         
36178         this.indicator = this.indicatorEl();
36179         
36180         if(this.indicator){
36181             this.indicator.addClass('invisible');
36182         }
36183         
36184         this.originalValue = this.getValue();
36185         
36186     },
36187     
36188     inputEl: function ()
36189     {
36190         return this.el.select('.roo-radio-set-input', true).first();
36191     },
36192     
36193     getChildContainer : function()
36194     {
36195         return this.itemsEl;
36196     },
36197     
36198     register : function(item)
36199     {
36200         this.radioes.push(item);
36201         
36202     },
36203     
36204     validate : function()
36205     {   
36206         if(this.getVisibilityEl().hasClass('hidden')){
36207             return true;
36208         }
36209         
36210         var valid = false;
36211         
36212         Roo.each(this.radioes, function(i){
36213             if(!i.checked){
36214                 return;
36215             }
36216             
36217             valid = true;
36218             return false;
36219         });
36220         
36221         if(this.allowBlank) {
36222             return true;
36223         }
36224         
36225         if(this.disabled || valid){
36226             this.markValid();
36227             return true;
36228         }
36229         
36230         this.markInvalid();
36231         return false;
36232         
36233     },
36234     
36235     markValid : function()
36236     {
36237         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36238             this.indicatorEl().removeClass('visible');
36239             this.indicatorEl().addClass('invisible');
36240         }
36241         
36242         
36243         if (Roo.bootstrap.version == 3) {
36244             this.el.removeClass([this.invalidClass, this.validClass]);
36245             this.el.addClass(this.validClass);
36246         } else {
36247             this.el.removeClass(['is-invalid','is-valid']);
36248             this.el.addClass(['is-valid']);
36249         }
36250         this.fireEvent('valid', this);
36251     },
36252     
36253     markInvalid : function(msg)
36254     {
36255         if(this.allowBlank || this.disabled){
36256             return;
36257         }
36258         
36259         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36260             this.indicatorEl().removeClass('invisible');
36261             this.indicatorEl().addClass('visible');
36262         }
36263         if (Roo.bootstrap.version == 3) {
36264             this.el.removeClass([this.invalidClass, this.validClass]);
36265             this.el.addClass(this.invalidClass);
36266         } else {
36267             this.el.removeClass(['is-invalid','is-valid']);
36268             this.el.addClass(['is-invalid']);
36269         }
36270         
36271         this.fireEvent('invalid', this, msg);
36272         
36273     },
36274     
36275     setValue : function(v, suppressEvent)
36276     {   
36277         if(this.value === v){
36278             return;
36279         }
36280         
36281         this.value = v;
36282         
36283         if(this.rendered){
36284             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36285         }
36286         
36287         Roo.each(this.radioes, function(i){
36288             i.checked = false;
36289             i.el.removeClass('checked');
36290         });
36291         
36292         Roo.each(this.radioes, function(i){
36293             
36294             if(i.value === v || i.value.toString() === v.toString()){
36295                 i.checked = true;
36296                 i.el.addClass('checked');
36297                 
36298                 if(suppressEvent !== true){
36299                     this.fireEvent('check', this, i);
36300                 }
36301                 
36302                 return false;
36303             }
36304             
36305         }, this);
36306         
36307         this.validate();
36308     },
36309     
36310     clearInvalid : function(){
36311         
36312         if(!this.el || this.preventMark){
36313             return;
36314         }
36315         
36316         this.el.removeClass([this.invalidClass]);
36317         
36318         this.fireEvent('valid', this);
36319     }
36320     
36321 });
36322
36323 Roo.apply(Roo.bootstrap.RadioSet, {
36324     
36325     groups: {},
36326     
36327     register : function(set)
36328     {
36329         this.groups[set.name] = set;
36330     },
36331     
36332     get: function(name) 
36333     {
36334         if (typeof(this.groups[name]) == 'undefined') {
36335             return false;
36336         }
36337         
36338         return this.groups[name] ;
36339     }
36340     
36341 });
36342 /*
36343  * Based on:
36344  * Ext JS Library 1.1.1
36345  * Copyright(c) 2006-2007, Ext JS, LLC.
36346  *
36347  * Originally Released Under LGPL - original licence link has changed is not relivant.
36348  *
36349  * Fork - LGPL
36350  * <script type="text/javascript">
36351  */
36352
36353
36354 /**
36355  * @class Roo.bootstrap.SplitBar
36356  * @extends Roo.util.Observable
36357  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36358  * <br><br>
36359  * Usage:
36360  * <pre><code>
36361 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36362                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36363 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36364 split.minSize = 100;
36365 split.maxSize = 600;
36366 split.animate = true;
36367 split.on('moved', splitterMoved);
36368 </code></pre>
36369  * @constructor
36370  * Create a new SplitBar
36371  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36372  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36373  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36374  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36375                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36376                         position of the SplitBar).
36377  */
36378 Roo.bootstrap.SplitBar = function(cfg){
36379     
36380     /** @private */
36381     
36382     //{
36383     //  dragElement : elm
36384     //  resizingElement: el,
36385         // optional..
36386     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36387     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36388         // existingProxy ???
36389     //}
36390     
36391     this.el = Roo.get(cfg.dragElement, true);
36392     this.el.dom.unselectable = "on";
36393     /** @private */
36394     this.resizingEl = Roo.get(cfg.resizingElement, true);
36395
36396     /**
36397      * @private
36398      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36399      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36400      * @type Number
36401      */
36402     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36403     
36404     /**
36405      * The minimum size of the resizing element. (Defaults to 0)
36406      * @type Number
36407      */
36408     this.minSize = 0;
36409     
36410     /**
36411      * The maximum size of the resizing element. (Defaults to 2000)
36412      * @type Number
36413      */
36414     this.maxSize = 2000;
36415     
36416     /**
36417      * Whether to animate the transition to the new size
36418      * @type Boolean
36419      */
36420     this.animate = false;
36421     
36422     /**
36423      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36424      * @type Boolean
36425      */
36426     this.useShim = false;
36427     
36428     /** @private */
36429     this.shim = null;
36430     
36431     if(!cfg.existingProxy){
36432         /** @private */
36433         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36434     }else{
36435         this.proxy = Roo.get(cfg.existingProxy).dom;
36436     }
36437     /** @private */
36438     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36439     
36440     /** @private */
36441     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36442     
36443     /** @private */
36444     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36445     
36446     /** @private */
36447     this.dragSpecs = {};
36448     
36449     /**
36450      * @private The adapter to use to positon and resize elements
36451      */
36452     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36453     this.adapter.init(this);
36454     
36455     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36456         /** @private */
36457         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36458         this.el.addClass("roo-splitbar-h");
36459     }else{
36460         /** @private */
36461         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36462         this.el.addClass("roo-splitbar-v");
36463     }
36464     
36465     this.addEvents({
36466         /**
36467          * @event resize
36468          * Fires when the splitter is moved (alias for {@link #event-moved})
36469          * @param {Roo.bootstrap.SplitBar} this
36470          * @param {Number} newSize the new width or height
36471          */
36472         "resize" : true,
36473         /**
36474          * @event moved
36475          * Fires when the splitter is moved
36476          * @param {Roo.bootstrap.SplitBar} this
36477          * @param {Number} newSize the new width or height
36478          */
36479         "moved" : true,
36480         /**
36481          * @event beforeresize
36482          * Fires before the splitter is dragged
36483          * @param {Roo.bootstrap.SplitBar} this
36484          */
36485         "beforeresize" : true,
36486
36487         "beforeapply" : true
36488     });
36489
36490     Roo.util.Observable.call(this);
36491 };
36492
36493 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36494     onStartProxyDrag : function(x, y){
36495         this.fireEvent("beforeresize", this);
36496         if(!this.overlay){
36497             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36498             o.unselectable();
36499             o.enableDisplayMode("block");
36500             // all splitbars share the same overlay
36501             Roo.bootstrap.SplitBar.prototype.overlay = o;
36502         }
36503         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36504         this.overlay.show();
36505         Roo.get(this.proxy).setDisplayed("block");
36506         var size = this.adapter.getElementSize(this);
36507         this.activeMinSize = this.getMinimumSize();;
36508         this.activeMaxSize = this.getMaximumSize();;
36509         var c1 = size - this.activeMinSize;
36510         var c2 = Math.max(this.activeMaxSize - size, 0);
36511         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36512             this.dd.resetConstraints();
36513             this.dd.setXConstraint(
36514                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36515                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36516             );
36517             this.dd.setYConstraint(0, 0);
36518         }else{
36519             this.dd.resetConstraints();
36520             this.dd.setXConstraint(0, 0);
36521             this.dd.setYConstraint(
36522                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36523                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36524             );
36525          }
36526         this.dragSpecs.startSize = size;
36527         this.dragSpecs.startPoint = [x, y];
36528         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36529     },
36530     
36531     /** 
36532      * @private Called after the drag operation by the DDProxy
36533      */
36534     onEndProxyDrag : function(e){
36535         Roo.get(this.proxy).setDisplayed(false);
36536         var endPoint = Roo.lib.Event.getXY(e);
36537         if(this.overlay){
36538             this.overlay.hide();
36539         }
36540         var newSize;
36541         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36542             newSize = this.dragSpecs.startSize + 
36543                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36544                     endPoint[0] - this.dragSpecs.startPoint[0] :
36545                     this.dragSpecs.startPoint[0] - endPoint[0]
36546                 );
36547         }else{
36548             newSize = this.dragSpecs.startSize + 
36549                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36550                     endPoint[1] - this.dragSpecs.startPoint[1] :
36551                     this.dragSpecs.startPoint[1] - endPoint[1]
36552                 );
36553         }
36554         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36555         if(newSize != this.dragSpecs.startSize){
36556             if(this.fireEvent('beforeapply', this, newSize) !== false){
36557                 this.adapter.setElementSize(this, newSize);
36558                 this.fireEvent("moved", this, newSize);
36559                 this.fireEvent("resize", this, newSize);
36560             }
36561         }
36562     },
36563     
36564     /**
36565      * Get the adapter this SplitBar uses
36566      * @return The adapter object
36567      */
36568     getAdapter : function(){
36569         return this.adapter;
36570     },
36571     
36572     /**
36573      * Set the adapter this SplitBar uses
36574      * @param {Object} adapter A SplitBar adapter object
36575      */
36576     setAdapter : function(adapter){
36577         this.adapter = adapter;
36578         this.adapter.init(this);
36579     },
36580     
36581     /**
36582      * Gets the minimum size for the resizing element
36583      * @return {Number} The minimum size
36584      */
36585     getMinimumSize : function(){
36586         return this.minSize;
36587     },
36588     
36589     /**
36590      * Sets the minimum size for the resizing element
36591      * @param {Number} minSize The minimum size
36592      */
36593     setMinimumSize : function(minSize){
36594         this.minSize = minSize;
36595     },
36596     
36597     /**
36598      * Gets the maximum size for the resizing element
36599      * @return {Number} The maximum size
36600      */
36601     getMaximumSize : function(){
36602         return this.maxSize;
36603     },
36604     
36605     /**
36606      * Sets the maximum size for the resizing element
36607      * @param {Number} maxSize The maximum size
36608      */
36609     setMaximumSize : function(maxSize){
36610         this.maxSize = maxSize;
36611     },
36612     
36613     /**
36614      * Sets the initialize size for the resizing element
36615      * @param {Number} size The initial size
36616      */
36617     setCurrentSize : function(size){
36618         var oldAnimate = this.animate;
36619         this.animate = false;
36620         this.adapter.setElementSize(this, size);
36621         this.animate = oldAnimate;
36622     },
36623     
36624     /**
36625      * Destroy this splitbar. 
36626      * @param {Boolean} removeEl True to remove the element
36627      */
36628     destroy : function(removeEl){
36629         if(this.shim){
36630             this.shim.remove();
36631         }
36632         this.dd.unreg();
36633         this.proxy.parentNode.removeChild(this.proxy);
36634         if(removeEl){
36635             this.el.remove();
36636         }
36637     }
36638 });
36639
36640 /**
36641  * @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.
36642  */
36643 Roo.bootstrap.SplitBar.createProxy = function(dir){
36644     var proxy = new Roo.Element(document.createElement("div"));
36645     proxy.unselectable();
36646     var cls = 'roo-splitbar-proxy';
36647     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36648     document.body.appendChild(proxy.dom);
36649     return proxy.dom;
36650 };
36651
36652 /** 
36653  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36654  * Default Adapter. It assumes the splitter and resizing element are not positioned
36655  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36656  */
36657 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36658 };
36659
36660 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36661     // do nothing for now
36662     init : function(s){
36663     
36664     },
36665     /**
36666      * Called before drag operations to get the current size of the resizing element. 
36667      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36668      */
36669      getElementSize : function(s){
36670         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36671             return s.resizingEl.getWidth();
36672         }else{
36673             return s.resizingEl.getHeight();
36674         }
36675     },
36676     
36677     /**
36678      * Called after drag operations to set the size of the resizing element.
36679      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36680      * @param {Number} newSize The new size to set
36681      * @param {Function} onComplete A function to be invoked when resizing is complete
36682      */
36683     setElementSize : function(s, newSize, onComplete){
36684         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36685             if(!s.animate){
36686                 s.resizingEl.setWidth(newSize);
36687                 if(onComplete){
36688                     onComplete(s, newSize);
36689                 }
36690             }else{
36691                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36692             }
36693         }else{
36694             
36695             if(!s.animate){
36696                 s.resizingEl.setHeight(newSize);
36697                 if(onComplete){
36698                     onComplete(s, newSize);
36699                 }
36700             }else{
36701                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36702             }
36703         }
36704     }
36705 };
36706
36707 /** 
36708  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36709  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36710  * Adapter that  moves the splitter element to align with the resized sizing element. 
36711  * Used with an absolute positioned SplitBar.
36712  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36713  * document.body, make sure you assign an id to the body element.
36714  */
36715 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36716     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36717     this.container = Roo.get(container);
36718 };
36719
36720 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36721     init : function(s){
36722         this.basic.init(s);
36723     },
36724     
36725     getElementSize : function(s){
36726         return this.basic.getElementSize(s);
36727     },
36728     
36729     setElementSize : function(s, newSize, onComplete){
36730         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36731     },
36732     
36733     moveSplitter : function(s){
36734         var yes = Roo.bootstrap.SplitBar;
36735         switch(s.placement){
36736             case yes.LEFT:
36737                 s.el.setX(s.resizingEl.getRight());
36738                 break;
36739             case yes.RIGHT:
36740                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36741                 break;
36742             case yes.TOP:
36743                 s.el.setY(s.resizingEl.getBottom());
36744                 break;
36745             case yes.BOTTOM:
36746                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36747                 break;
36748         }
36749     }
36750 };
36751
36752 /**
36753  * Orientation constant - Create a vertical SplitBar
36754  * @static
36755  * @type Number
36756  */
36757 Roo.bootstrap.SplitBar.VERTICAL = 1;
36758
36759 /**
36760  * Orientation constant - Create a horizontal SplitBar
36761  * @static
36762  * @type Number
36763  */
36764 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36765
36766 /**
36767  * Placement constant - The resizing element is to the left of the splitter element
36768  * @static
36769  * @type Number
36770  */
36771 Roo.bootstrap.SplitBar.LEFT = 1;
36772
36773 /**
36774  * Placement constant - The resizing element is to the right of the splitter element
36775  * @static
36776  * @type Number
36777  */
36778 Roo.bootstrap.SplitBar.RIGHT = 2;
36779
36780 /**
36781  * Placement constant - The resizing element is positioned above the splitter element
36782  * @static
36783  * @type Number
36784  */
36785 Roo.bootstrap.SplitBar.TOP = 3;
36786
36787 /**
36788  * Placement constant - The resizing element is positioned under splitter element
36789  * @static
36790  * @type Number
36791  */
36792 Roo.bootstrap.SplitBar.BOTTOM = 4;
36793 Roo.namespace("Roo.bootstrap.layout");/*
36794  * Based on:
36795  * Ext JS Library 1.1.1
36796  * Copyright(c) 2006-2007, Ext JS, LLC.
36797  *
36798  * Originally Released Under LGPL - original licence link has changed is not relivant.
36799  *
36800  * Fork - LGPL
36801  * <script type="text/javascript">
36802  */
36803
36804 /**
36805  * @class Roo.bootstrap.layout.Manager
36806  * @extends Roo.bootstrap.Component
36807  * Base class for layout managers.
36808  */
36809 Roo.bootstrap.layout.Manager = function(config)
36810 {
36811     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36812
36813
36814
36815
36816
36817     /** false to disable window resize monitoring @type Boolean */
36818     this.monitorWindowResize = true;
36819     this.regions = {};
36820     this.addEvents({
36821         /**
36822          * @event layout
36823          * Fires when a layout is performed.
36824          * @param {Roo.LayoutManager} this
36825          */
36826         "layout" : true,
36827         /**
36828          * @event regionresized
36829          * Fires when the user resizes a region.
36830          * @param {Roo.LayoutRegion} region The resized region
36831          * @param {Number} newSize The new size (width for east/west, height for north/south)
36832          */
36833         "regionresized" : true,
36834         /**
36835          * @event regioncollapsed
36836          * Fires when a region is collapsed.
36837          * @param {Roo.LayoutRegion} region The collapsed region
36838          */
36839         "regioncollapsed" : true,
36840         /**
36841          * @event regionexpanded
36842          * Fires when a region is expanded.
36843          * @param {Roo.LayoutRegion} region The expanded region
36844          */
36845         "regionexpanded" : true
36846     });
36847     this.updating = false;
36848
36849     if (config.el) {
36850         this.el = Roo.get(config.el);
36851         this.initEvents();
36852     }
36853
36854 };
36855
36856 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36857
36858
36859     regions : null,
36860
36861     monitorWindowResize : true,
36862
36863
36864     updating : false,
36865
36866
36867     onRender : function(ct, position)
36868     {
36869         if(!this.el){
36870             this.el = Roo.get(ct);
36871             this.initEvents();
36872         }
36873         //this.fireEvent('render',this);
36874     },
36875
36876
36877     initEvents: function()
36878     {
36879
36880
36881         // ie scrollbar fix
36882         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36883             document.body.scroll = "no";
36884         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36885             this.el.position('relative');
36886         }
36887         this.id = this.el.id;
36888         this.el.addClass("roo-layout-container");
36889         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36890         if(this.el.dom != document.body ) {
36891             this.el.on('resize', this.layout,this);
36892             this.el.on('show', this.layout,this);
36893         }
36894
36895     },
36896
36897     /**
36898      * Returns true if this layout is currently being updated
36899      * @return {Boolean}
36900      */
36901     isUpdating : function(){
36902         return this.updating;
36903     },
36904
36905     /**
36906      * Suspend the LayoutManager from doing auto-layouts while
36907      * making multiple add or remove calls
36908      */
36909     beginUpdate : function(){
36910         this.updating = true;
36911     },
36912
36913     /**
36914      * Restore auto-layouts and optionally disable the manager from performing a layout
36915      * @param {Boolean} noLayout true to disable a layout update
36916      */
36917     endUpdate : function(noLayout){
36918         this.updating = false;
36919         if(!noLayout){
36920             this.layout();
36921         }
36922     },
36923
36924     layout: function(){
36925         // abstract...
36926     },
36927
36928     onRegionResized : function(region, newSize){
36929         this.fireEvent("regionresized", region, newSize);
36930         this.layout();
36931     },
36932
36933     onRegionCollapsed : function(region){
36934         this.fireEvent("regioncollapsed", region);
36935     },
36936
36937     onRegionExpanded : function(region){
36938         this.fireEvent("regionexpanded", region);
36939     },
36940
36941     /**
36942      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36943      * performs box-model adjustments.
36944      * @return {Object} The size as an object {width: (the width), height: (the height)}
36945      */
36946     getViewSize : function()
36947     {
36948         var size;
36949         if(this.el.dom != document.body){
36950             size = this.el.getSize();
36951         }else{
36952             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36953         }
36954         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36955         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36956         return size;
36957     },
36958
36959     /**
36960      * Returns the Element this layout is bound to.
36961      * @return {Roo.Element}
36962      */
36963     getEl : function(){
36964         return this.el;
36965     },
36966
36967     /**
36968      * Returns the specified region.
36969      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36970      * @return {Roo.LayoutRegion}
36971      */
36972     getRegion : function(target){
36973         return this.regions[target.toLowerCase()];
36974     },
36975
36976     onWindowResize : function(){
36977         if(this.monitorWindowResize){
36978             this.layout();
36979         }
36980     }
36981 });
36982 /*
36983  * Based on:
36984  * Ext JS Library 1.1.1
36985  * Copyright(c) 2006-2007, Ext JS, LLC.
36986  *
36987  * Originally Released Under LGPL - original licence link has changed is not relivant.
36988  *
36989  * Fork - LGPL
36990  * <script type="text/javascript">
36991  */
36992 /**
36993  * @class Roo.bootstrap.layout.Border
36994  * @extends Roo.bootstrap.layout.Manager
36995  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36996  * please see: examples/bootstrap/nested.html<br><br>
36997  
36998 <b>The container the layout is rendered into can be either the body element or any other element.
36999 If it is not the body element, the container needs to either be an absolute positioned element,
37000 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37001 the container size if it is not the body element.</b>
37002
37003 * @constructor
37004 * Create a new Border
37005 * @param {Object} config Configuration options
37006  */
37007 Roo.bootstrap.layout.Border = function(config){
37008     config = config || {};
37009     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37010     
37011     
37012     
37013     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37014         if(config[region]){
37015             config[region].region = region;
37016             this.addRegion(config[region]);
37017         }
37018     },this);
37019     
37020 };
37021
37022 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37023
37024 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37025     
37026     parent : false, // this might point to a 'nest' or a ???
37027     
37028     /**
37029      * Creates and adds a new region if it doesn't already exist.
37030      * @param {String} target The target region key (north, south, east, west or center).
37031      * @param {Object} config The regions config object
37032      * @return {BorderLayoutRegion} The new region
37033      */
37034     addRegion : function(config)
37035     {
37036         if(!this.regions[config.region]){
37037             var r = this.factory(config);
37038             this.bindRegion(r);
37039         }
37040         return this.regions[config.region];
37041     },
37042
37043     // private (kinda)
37044     bindRegion : function(r){
37045         this.regions[r.config.region] = r;
37046         
37047         r.on("visibilitychange",    this.layout, this);
37048         r.on("paneladded",          this.layout, this);
37049         r.on("panelremoved",        this.layout, this);
37050         r.on("invalidated",         this.layout, this);
37051         r.on("resized",             this.onRegionResized, this);
37052         r.on("collapsed",           this.onRegionCollapsed, this);
37053         r.on("expanded",            this.onRegionExpanded, this);
37054     },
37055
37056     /**
37057      * Performs a layout update.
37058      */
37059     layout : function()
37060     {
37061         if(this.updating) {
37062             return;
37063         }
37064         
37065         // render all the rebions if they have not been done alreayd?
37066         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37067             if(this.regions[region] && !this.regions[region].bodyEl){
37068                 this.regions[region].onRender(this.el)
37069             }
37070         },this);
37071         
37072         var size = this.getViewSize();
37073         var w = size.width;
37074         var h = size.height;
37075         var centerW = w;
37076         var centerH = h;
37077         var centerY = 0;
37078         var centerX = 0;
37079         //var x = 0, y = 0;
37080
37081         var rs = this.regions;
37082         var north = rs["north"];
37083         var south = rs["south"]; 
37084         var west = rs["west"];
37085         var east = rs["east"];
37086         var center = rs["center"];
37087         //if(this.hideOnLayout){ // not supported anymore
37088             //c.el.setStyle("display", "none");
37089         //}
37090         if(north && north.isVisible()){
37091             var b = north.getBox();
37092             var m = north.getMargins();
37093             b.width = w - (m.left+m.right);
37094             b.x = m.left;
37095             b.y = m.top;
37096             centerY = b.height + b.y + m.bottom;
37097             centerH -= centerY;
37098             north.updateBox(this.safeBox(b));
37099         }
37100         if(south && south.isVisible()){
37101             var b = south.getBox();
37102             var m = south.getMargins();
37103             b.width = w - (m.left+m.right);
37104             b.x = m.left;
37105             var totalHeight = (b.height + m.top + m.bottom);
37106             b.y = h - totalHeight + m.top;
37107             centerH -= totalHeight;
37108             south.updateBox(this.safeBox(b));
37109         }
37110         if(west && west.isVisible()){
37111             var b = west.getBox();
37112             var m = west.getMargins();
37113             b.height = centerH - (m.top+m.bottom);
37114             b.x = m.left;
37115             b.y = centerY + m.top;
37116             var totalWidth = (b.width + m.left + m.right);
37117             centerX += totalWidth;
37118             centerW -= totalWidth;
37119             west.updateBox(this.safeBox(b));
37120         }
37121         if(east && east.isVisible()){
37122             var b = east.getBox();
37123             var m = east.getMargins();
37124             b.height = centerH - (m.top+m.bottom);
37125             var totalWidth = (b.width + m.left + m.right);
37126             b.x = w - totalWidth + m.left;
37127             b.y = centerY + m.top;
37128             centerW -= totalWidth;
37129             east.updateBox(this.safeBox(b));
37130         }
37131         if(center){
37132             var m = center.getMargins();
37133             var centerBox = {
37134                 x: centerX + m.left,
37135                 y: centerY + m.top,
37136                 width: centerW - (m.left+m.right),
37137                 height: centerH - (m.top+m.bottom)
37138             };
37139             //if(this.hideOnLayout){
37140                 //center.el.setStyle("display", "block");
37141             //}
37142             center.updateBox(this.safeBox(centerBox));
37143         }
37144         this.el.repaint();
37145         this.fireEvent("layout", this);
37146     },
37147
37148     // private
37149     safeBox : function(box){
37150         box.width = Math.max(0, box.width);
37151         box.height = Math.max(0, box.height);
37152         return box;
37153     },
37154
37155     /**
37156      * Adds a ContentPanel (or subclass) to this layout.
37157      * @param {String} target The target region key (north, south, east, west or center).
37158      * @param {Roo.ContentPanel} panel The panel to add
37159      * @return {Roo.ContentPanel} The added panel
37160      */
37161     add : function(target, panel){
37162          
37163         target = target.toLowerCase();
37164         return this.regions[target].add(panel);
37165     },
37166
37167     /**
37168      * Remove a ContentPanel (or subclass) to this layout.
37169      * @param {String} target The target region key (north, south, east, west or center).
37170      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37171      * @return {Roo.ContentPanel} The removed panel
37172      */
37173     remove : function(target, panel){
37174         target = target.toLowerCase();
37175         return this.regions[target].remove(panel);
37176     },
37177
37178     /**
37179      * Searches all regions for a panel with the specified id
37180      * @param {String} panelId
37181      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37182      */
37183     findPanel : function(panelId){
37184         var rs = this.regions;
37185         for(var target in rs){
37186             if(typeof rs[target] != "function"){
37187                 var p = rs[target].getPanel(panelId);
37188                 if(p){
37189                     return p;
37190                 }
37191             }
37192         }
37193         return null;
37194     },
37195
37196     /**
37197      * Searches all regions for a panel with the specified id and activates (shows) it.
37198      * @param {String/ContentPanel} panelId The panels id or the panel itself
37199      * @return {Roo.ContentPanel} The shown panel or null
37200      */
37201     showPanel : function(panelId) {
37202       var rs = this.regions;
37203       for(var target in rs){
37204          var r = rs[target];
37205          if(typeof r != "function"){
37206             if(r.hasPanel(panelId)){
37207                return r.showPanel(panelId);
37208             }
37209          }
37210       }
37211       return null;
37212    },
37213
37214    /**
37215      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37216      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37217      */
37218    /*
37219     restoreState : function(provider){
37220         if(!provider){
37221             provider = Roo.state.Manager;
37222         }
37223         var sm = new Roo.LayoutStateManager();
37224         sm.init(this, provider);
37225     },
37226 */
37227  
37228  
37229     /**
37230      * Adds a xtype elements to the layout.
37231      * <pre><code>
37232
37233 layout.addxtype({
37234        xtype : 'ContentPanel',
37235        region: 'west',
37236        items: [ .... ]
37237    }
37238 );
37239
37240 layout.addxtype({
37241         xtype : 'NestedLayoutPanel',
37242         region: 'west',
37243         layout: {
37244            center: { },
37245            west: { }   
37246         },
37247         items : [ ... list of content panels or nested layout panels.. ]
37248    }
37249 );
37250 </code></pre>
37251      * @param {Object} cfg Xtype definition of item to add.
37252      */
37253     addxtype : function(cfg)
37254     {
37255         // basically accepts a pannel...
37256         // can accept a layout region..!?!?
37257         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37258         
37259         
37260         // theory?  children can only be panels??
37261         
37262         //if (!cfg.xtype.match(/Panel$/)) {
37263         //    return false;
37264         //}
37265         var ret = false;
37266         
37267         if (typeof(cfg.region) == 'undefined') {
37268             Roo.log("Failed to add Panel, region was not set");
37269             Roo.log(cfg);
37270             return false;
37271         }
37272         var region = cfg.region;
37273         delete cfg.region;
37274         
37275           
37276         var xitems = [];
37277         if (cfg.items) {
37278             xitems = cfg.items;
37279             delete cfg.items;
37280         }
37281         var nb = false;
37282         
37283         if ( region == 'center') {
37284             Roo.log("Center: " + cfg.title);
37285         }
37286         
37287         
37288         switch(cfg.xtype) 
37289         {
37290             case 'Content':  // ContentPanel (el, cfg)
37291             case 'Scroll':  // ContentPanel (el, cfg)
37292             case 'View': 
37293                 cfg.autoCreate = cfg.autoCreate || true;
37294                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37295                 //} else {
37296                 //    var el = this.el.createChild();
37297                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37298                 //}
37299                 
37300                 this.add(region, ret);
37301                 break;
37302             
37303             /*
37304             case 'TreePanel': // our new panel!
37305                 cfg.el = this.el.createChild();
37306                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37307                 this.add(region, ret);
37308                 break;
37309             */
37310             
37311             case 'Nest': 
37312                 // create a new Layout (which is  a Border Layout...
37313                 
37314                 var clayout = cfg.layout;
37315                 clayout.el  = this.el.createChild();
37316                 clayout.items   = clayout.items  || [];
37317                 
37318                 delete cfg.layout;
37319                 
37320                 // replace this exitems with the clayout ones..
37321                 xitems = clayout.items;
37322                  
37323                 // force background off if it's in center...
37324                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37325                     cfg.background = false;
37326                 }
37327                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37328                 
37329                 
37330                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37331                 //console.log('adding nested layout panel '  + cfg.toSource());
37332                 this.add(region, ret);
37333                 nb = {}; /// find first...
37334                 break;
37335             
37336             case 'Grid':
37337                 
37338                 // needs grid and region
37339                 
37340                 //var el = this.getRegion(region).el.createChild();
37341                 /*
37342                  *var el = this.el.createChild();
37343                 // create the grid first...
37344                 cfg.grid.container = el;
37345                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37346                 */
37347                 
37348                 if (region == 'center' && this.active ) {
37349                     cfg.background = false;
37350                 }
37351                 
37352                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37353                 
37354                 this.add(region, ret);
37355                 /*
37356                 if (cfg.background) {
37357                     // render grid on panel activation (if panel background)
37358                     ret.on('activate', function(gp) {
37359                         if (!gp.grid.rendered) {
37360                     //        gp.grid.render(el);
37361                         }
37362                     });
37363                 } else {
37364                   //  cfg.grid.render(el);
37365                 }
37366                 */
37367                 break;
37368            
37369            
37370             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37371                 // it was the old xcomponent building that caused this before.
37372                 // espeically if border is the top element in the tree.
37373                 ret = this;
37374                 break; 
37375                 
37376                     
37377                 
37378                 
37379                 
37380             default:
37381                 /*
37382                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37383                     
37384                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37385                     this.add(region, ret);
37386                 } else {
37387                 */
37388                     Roo.log(cfg);
37389                     throw "Can not add '" + cfg.xtype + "' to Border";
37390                     return null;
37391              
37392                                 
37393              
37394         }
37395         this.beginUpdate();
37396         // add children..
37397         var region = '';
37398         var abn = {};
37399         Roo.each(xitems, function(i)  {
37400             region = nb && i.region ? i.region : false;
37401             
37402             var add = ret.addxtype(i);
37403            
37404             if (region) {
37405                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37406                 if (!i.background) {
37407                     abn[region] = nb[region] ;
37408                 }
37409             }
37410             
37411         });
37412         this.endUpdate();
37413
37414         // make the last non-background panel active..
37415         //if (nb) { Roo.log(abn); }
37416         if (nb) {
37417             
37418             for(var r in abn) {
37419                 region = this.getRegion(r);
37420                 if (region) {
37421                     // tried using nb[r], but it does not work..
37422                      
37423                     region.showPanel(abn[r]);
37424                    
37425                 }
37426             }
37427         }
37428         return ret;
37429         
37430     },
37431     
37432     
37433 // private
37434     factory : function(cfg)
37435     {
37436         
37437         var validRegions = Roo.bootstrap.layout.Border.regions;
37438
37439         var target = cfg.region;
37440         cfg.mgr = this;
37441         
37442         var r = Roo.bootstrap.layout;
37443         Roo.log(target);
37444         switch(target){
37445             case "north":
37446                 return new r.North(cfg);
37447             case "south":
37448                 return new r.South(cfg);
37449             case "east":
37450                 return new r.East(cfg);
37451             case "west":
37452                 return new r.West(cfg);
37453             case "center":
37454                 return new r.Center(cfg);
37455         }
37456         throw 'Layout region "'+target+'" not supported.';
37457     }
37458     
37459     
37460 });
37461  /*
37462  * Based on:
37463  * Ext JS Library 1.1.1
37464  * Copyright(c) 2006-2007, Ext JS, LLC.
37465  *
37466  * Originally Released Under LGPL - original licence link has changed is not relivant.
37467  *
37468  * Fork - LGPL
37469  * <script type="text/javascript">
37470  */
37471  
37472 /**
37473  * @class Roo.bootstrap.layout.Basic
37474  * @extends Roo.util.Observable
37475  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37476  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37477  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37478  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37479  * @cfg {string}   region  the region that it inhabits..
37480  * @cfg {bool}   skipConfig skip config?
37481  * 
37482
37483  */
37484 Roo.bootstrap.layout.Basic = function(config){
37485     
37486     this.mgr = config.mgr;
37487     
37488     this.position = config.region;
37489     
37490     var skipConfig = config.skipConfig;
37491     
37492     this.events = {
37493         /**
37494          * @scope Roo.BasicLayoutRegion
37495          */
37496         
37497         /**
37498          * @event beforeremove
37499          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37500          * @param {Roo.LayoutRegion} this
37501          * @param {Roo.ContentPanel} panel The panel
37502          * @param {Object} e The cancel event object
37503          */
37504         "beforeremove" : true,
37505         /**
37506          * @event invalidated
37507          * Fires when the layout for this region is changed.
37508          * @param {Roo.LayoutRegion} this
37509          */
37510         "invalidated" : true,
37511         /**
37512          * @event visibilitychange
37513          * Fires when this region is shown or hidden 
37514          * @param {Roo.LayoutRegion} this
37515          * @param {Boolean} visibility true or false
37516          */
37517         "visibilitychange" : true,
37518         /**
37519          * @event paneladded
37520          * Fires when a panel is added. 
37521          * @param {Roo.LayoutRegion} this
37522          * @param {Roo.ContentPanel} panel The panel
37523          */
37524         "paneladded" : true,
37525         /**
37526          * @event panelremoved
37527          * Fires when a panel is removed. 
37528          * @param {Roo.LayoutRegion} this
37529          * @param {Roo.ContentPanel} panel The panel
37530          */
37531         "panelremoved" : true,
37532         /**
37533          * @event beforecollapse
37534          * Fires when this region before collapse.
37535          * @param {Roo.LayoutRegion} this
37536          */
37537         "beforecollapse" : true,
37538         /**
37539          * @event collapsed
37540          * Fires when this region is collapsed.
37541          * @param {Roo.LayoutRegion} this
37542          */
37543         "collapsed" : true,
37544         /**
37545          * @event expanded
37546          * Fires when this region is expanded.
37547          * @param {Roo.LayoutRegion} this
37548          */
37549         "expanded" : true,
37550         /**
37551          * @event slideshow
37552          * Fires when this region is slid into view.
37553          * @param {Roo.LayoutRegion} this
37554          */
37555         "slideshow" : true,
37556         /**
37557          * @event slidehide
37558          * Fires when this region slides out of view. 
37559          * @param {Roo.LayoutRegion} this
37560          */
37561         "slidehide" : true,
37562         /**
37563          * @event panelactivated
37564          * Fires when a panel is activated. 
37565          * @param {Roo.LayoutRegion} this
37566          * @param {Roo.ContentPanel} panel The activated panel
37567          */
37568         "panelactivated" : true,
37569         /**
37570          * @event resized
37571          * Fires when the user resizes this region. 
37572          * @param {Roo.LayoutRegion} this
37573          * @param {Number} newSize The new size (width for east/west, height for north/south)
37574          */
37575         "resized" : true
37576     };
37577     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37578     this.panels = new Roo.util.MixedCollection();
37579     this.panels.getKey = this.getPanelId.createDelegate(this);
37580     this.box = null;
37581     this.activePanel = null;
37582     // ensure listeners are added...
37583     
37584     if (config.listeners || config.events) {
37585         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37586             listeners : config.listeners || {},
37587             events : config.events || {}
37588         });
37589     }
37590     
37591     if(skipConfig !== true){
37592         this.applyConfig(config);
37593     }
37594 };
37595
37596 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37597 {
37598     getPanelId : function(p){
37599         return p.getId();
37600     },
37601     
37602     applyConfig : function(config){
37603         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37604         this.config = config;
37605         
37606     },
37607     
37608     /**
37609      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37610      * the width, for horizontal (north, south) the height.
37611      * @param {Number} newSize The new width or height
37612      */
37613     resizeTo : function(newSize){
37614         var el = this.el ? this.el :
37615                  (this.activePanel ? this.activePanel.getEl() : null);
37616         if(el){
37617             switch(this.position){
37618                 case "east":
37619                 case "west":
37620                     el.setWidth(newSize);
37621                     this.fireEvent("resized", this, newSize);
37622                 break;
37623                 case "north":
37624                 case "south":
37625                     el.setHeight(newSize);
37626                     this.fireEvent("resized", this, newSize);
37627                 break;                
37628             }
37629         }
37630     },
37631     
37632     getBox : function(){
37633         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37634     },
37635     
37636     getMargins : function(){
37637         return this.margins;
37638     },
37639     
37640     updateBox : function(box){
37641         this.box = box;
37642         var el = this.activePanel.getEl();
37643         el.dom.style.left = box.x + "px";
37644         el.dom.style.top = box.y + "px";
37645         this.activePanel.setSize(box.width, box.height);
37646     },
37647     
37648     /**
37649      * Returns the container element for this region.
37650      * @return {Roo.Element}
37651      */
37652     getEl : function(){
37653         return this.activePanel;
37654     },
37655     
37656     /**
37657      * Returns true if this region is currently visible.
37658      * @return {Boolean}
37659      */
37660     isVisible : function(){
37661         return this.activePanel ? true : false;
37662     },
37663     
37664     setActivePanel : function(panel){
37665         panel = this.getPanel(panel);
37666         if(this.activePanel && this.activePanel != panel){
37667             this.activePanel.setActiveState(false);
37668             this.activePanel.getEl().setLeftTop(-10000,-10000);
37669         }
37670         this.activePanel = panel;
37671         panel.setActiveState(true);
37672         if(this.box){
37673             panel.setSize(this.box.width, this.box.height);
37674         }
37675         this.fireEvent("panelactivated", this, panel);
37676         this.fireEvent("invalidated");
37677     },
37678     
37679     /**
37680      * Show the specified panel.
37681      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37682      * @return {Roo.ContentPanel} The shown panel or null
37683      */
37684     showPanel : function(panel){
37685         panel = this.getPanel(panel);
37686         if(panel){
37687             this.setActivePanel(panel);
37688         }
37689         return panel;
37690     },
37691     
37692     /**
37693      * Get the active panel for this region.
37694      * @return {Roo.ContentPanel} The active panel or null
37695      */
37696     getActivePanel : function(){
37697         return this.activePanel;
37698     },
37699     
37700     /**
37701      * Add the passed ContentPanel(s)
37702      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37703      * @return {Roo.ContentPanel} The panel added (if only one was added)
37704      */
37705     add : function(panel){
37706         if(arguments.length > 1){
37707             for(var i = 0, len = arguments.length; i < len; i++) {
37708                 this.add(arguments[i]);
37709             }
37710             return null;
37711         }
37712         if(this.hasPanel(panel)){
37713             this.showPanel(panel);
37714             return panel;
37715         }
37716         var el = panel.getEl();
37717         if(el.dom.parentNode != this.mgr.el.dom){
37718             this.mgr.el.dom.appendChild(el.dom);
37719         }
37720         if(panel.setRegion){
37721             panel.setRegion(this);
37722         }
37723         this.panels.add(panel);
37724         el.setStyle("position", "absolute");
37725         if(!panel.background){
37726             this.setActivePanel(panel);
37727             if(this.config.initialSize && this.panels.getCount()==1){
37728                 this.resizeTo(this.config.initialSize);
37729             }
37730         }
37731         this.fireEvent("paneladded", this, panel);
37732         return panel;
37733     },
37734     
37735     /**
37736      * Returns true if the panel is in this region.
37737      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37738      * @return {Boolean}
37739      */
37740     hasPanel : function(panel){
37741         if(typeof panel == "object"){ // must be panel obj
37742             panel = panel.getId();
37743         }
37744         return this.getPanel(panel) ? true : false;
37745     },
37746     
37747     /**
37748      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37749      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37750      * @param {Boolean} preservePanel Overrides the config preservePanel option
37751      * @return {Roo.ContentPanel} The panel that was removed
37752      */
37753     remove : function(panel, preservePanel){
37754         panel = this.getPanel(panel);
37755         if(!panel){
37756             return null;
37757         }
37758         var e = {};
37759         this.fireEvent("beforeremove", this, panel, e);
37760         if(e.cancel === true){
37761             return null;
37762         }
37763         var panelId = panel.getId();
37764         this.panels.removeKey(panelId);
37765         return panel;
37766     },
37767     
37768     /**
37769      * Returns the panel specified or null if it's not in this region.
37770      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37771      * @return {Roo.ContentPanel}
37772      */
37773     getPanel : function(id){
37774         if(typeof id == "object"){ // must be panel obj
37775             return id;
37776         }
37777         return this.panels.get(id);
37778     },
37779     
37780     /**
37781      * Returns this regions position (north/south/east/west/center).
37782      * @return {String} 
37783      */
37784     getPosition: function(){
37785         return this.position;    
37786     }
37787 });/*
37788  * Based on:
37789  * Ext JS Library 1.1.1
37790  * Copyright(c) 2006-2007, Ext JS, LLC.
37791  *
37792  * Originally Released Under LGPL - original licence link has changed is not relivant.
37793  *
37794  * Fork - LGPL
37795  * <script type="text/javascript">
37796  */
37797  
37798 /**
37799  * @class Roo.bootstrap.layout.Region
37800  * @extends Roo.bootstrap.layout.Basic
37801  * This class represents a region in a layout manager.
37802  
37803  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37804  * @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})
37805  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37806  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37807  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37808  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37809  * @cfg {String}    title           The title for the region (overrides panel titles)
37810  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37811  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37812  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37813  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37814  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37815  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37816  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37817  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37818  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37819  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37820
37821  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37822  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37823  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37824  * @cfg {Number}    width           For East/West panels
37825  * @cfg {Number}    height          For North/South panels
37826  * @cfg {Boolean}   split           To show the splitter
37827  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37828  * 
37829  * @cfg {string}   cls             Extra CSS classes to add to region
37830  * 
37831  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37832  * @cfg {string}   region  the region that it inhabits..
37833  *
37834
37835  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37836  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37837
37838  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37839  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37840  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37841  */
37842 Roo.bootstrap.layout.Region = function(config)
37843 {
37844     this.applyConfig(config);
37845
37846     var mgr = config.mgr;
37847     var pos = config.region;
37848     config.skipConfig = true;
37849     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37850     
37851     if (mgr.el) {
37852         this.onRender(mgr.el);   
37853     }
37854      
37855     this.visible = true;
37856     this.collapsed = false;
37857     this.unrendered_panels = [];
37858 };
37859
37860 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37861
37862     position: '', // set by wrapper (eg. north/south etc..)
37863     unrendered_panels : null,  // unrendered panels.
37864     
37865     tabPosition : false,
37866     
37867     mgr: false, // points to 'Border'
37868     
37869     
37870     createBody : function(){
37871         /** This region's body element 
37872         * @type Roo.Element */
37873         this.bodyEl = this.el.createChild({
37874                 tag: "div",
37875                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37876         });
37877     },
37878
37879     onRender: function(ctr, pos)
37880     {
37881         var dh = Roo.DomHelper;
37882         /** This region's container element 
37883         * @type Roo.Element */
37884         this.el = dh.append(ctr.dom, {
37885                 tag: "div",
37886                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37887             }, true);
37888         /** This region's title element 
37889         * @type Roo.Element */
37890     
37891         this.titleEl = dh.append(this.el.dom,  {
37892                 tag: "div",
37893                 unselectable: "on",
37894                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37895                 children:[
37896                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37897                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37898                 ]
37899             }, true);
37900         
37901         this.titleEl.enableDisplayMode();
37902         /** This region's title text element 
37903         * @type HTMLElement */
37904         this.titleTextEl = this.titleEl.dom.firstChild;
37905         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37906         /*
37907         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37908         this.closeBtn.enableDisplayMode();
37909         this.closeBtn.on("click", this.closeClicked, this);
37910         this.closeBtn.hide();
37911     */
37912         this.createBody(this.config);
37913         if(this.config.hideWhenEmpty){
37914             this.hide();
37915             this.on("paneladded", this.validateVisibility, this);
37916             this.on("panelremoved", this.validateVisibility, this);
37917         }
37918         if(this.autoScroll){
37919             this.bodyEl.setStyle("overflow", "auto");
37920         }else{
37921             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37922         }
37923         //if(c.titlebar !== false){
37924             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37925                 this.titleEl.hide();
37926             }else{
37927                 this.titleEl.show();
37928                 if(this.config.title){
37929                     this.titleTextEl.innerHTML = this.config.title;
37930                 }
37931             }
37932         //}
37933         if(this.config.collapsed){
37934             this.collapse(true);
37935         }
37936         if(this.config.hidden){
37937             this.hide();
37938         }
37939         
37940         if (this.unrendered_panels && this.unrendered_panels.length) {
37941             for (var i =0;i< this.unrendered_panels.length; i++) {
37942                 this.add(this.unrendered_panels[i]);
37943             }
37944             this.unrendered_panels = null;
37945             
37946         }
37947         
37948     },
37949     
37950     applyConfig : function(c)
37951     {
37952         /*
37953          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37954             var dh = Roo.DomHelper;
37955             if(c.titlebar !== false){
37956                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37957                 this.collapseBtn.on("click", this.collapse, this);
37958                 this.collapseBtn.enableDisplayMode();
37959                 /*
37960                 if(c.showPin === true || this.showPin){
37961                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37962                     this.stickBtn.enableDisplayMode();
37963                     this.stickBtn.on("click", this.expand, this);
37964                     this.stickBtn.hide();
37965                 }
37966                 
37967             }
37968             */
37969             /** This region's collapsed element
37970             * @type Roo.Element */
37971             /*
37972              *
37973             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37974                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37975             ]}, true);
37976             
37977             if(c.floatable !== false){
37978                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37979                this.collapsedEl.on("click", this.collapseClick, this);
37980             }
37981
37982             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37983                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37984                    id: "message", unselectable: "on", style:{"float":"left"}});
37985                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37986              }
37987             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37988             this.expandBtn.on("click", this.expand, this);
37989             
37990         }
37991         
37992         if(this.collapseBtn){
37993             this.collapseBtn.setVisible(c.collapsible == true);
37994         }
37995         
37996         this.cmargins = c.cmargins || this.cmargins ||
37997                          (this.position == "west" || this.position == "east" ?
37998                              {top: 0, left: 2, right:2, bottom: 0} :
37999                              {top: 2, left: 0, right:0, bottom: 2});
38000         */
38001         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38002         
38003         
38004         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38005         
38006         this.autoScroll = c.autoScroll || false;
38007         
38008         
38009        
38010         
38011         this.duration = c.duration || .30;
38012         this.slideDuration = c.slideDuration || .45;
38013         this.config = c;
38014        
38015     },
38016     /**
38017      * Returns true if this region is currently visible.
38018      * @return {Boolean}
38019      */
38020     isVisible : function(){
38021         return this.visible;
38022     },
38023
38024     /**
38025      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38026      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38027      */
38028     //setCollapsedTitle : function(title){
38029     //    title = title || "&#160;";
38030      //   if(this.collapsedTitleTextEl){
38031       //      this.collapsedTitleTextEl.innerHTML = title;
38032        // }
38033     //},
38034
38035     getBox : function(){
38036         var b;
38037       //  if(!this.collapsed){
38038             b = this.el.getBox(false, true);
38039        // }else{
38040           //  b = this.collapsedEl.getBox(false, true);
38041         //}
38042         return b;
38043     },
38044
38045     getMargins : function(){
38046         return this.margins;
38047         //return this.collapsed ? this.cmargins : this.margins;
38048     },
38049 /*
38050     highlight : function(){
38051         this.el.addClass("x-layout-panel-dragover");
38052     },
38053
38054     unhighlight : function(){
38055         this.el.removeClass("x-layout-panel-dragover");
38056     },
38057 */
38058     updateBox : function(box)
38059     {
38060         if (!this.bodyEl) {
38061             return; // not rendered yet..
38062         }
38063         
38064         this.box = box;
38065         if(!this.collapsed){
38066             this.el.dom.style.left = box.x + "px";
38067             this.el.dom.style.top = box.y + "px";
38068             this.updateBody(box.width, box.height);
38069         }else{
38070             this.collapsedEl.dom.style.left = box.x + "px";
38071             this.collapsedEl.dom.style.top = box.y + "px";
38072             this.collapsedEl.setSize(box.width, box.height);
38073         }
38074         if(this.tabs){
38075             this.tabs.autoSizeTabs();
38076         }
38077     },
38078
38079     updateBody : function(w, h)
38080     {
38081         if(w !== null){
38082             this.el.setWidth(w);
38083             w -= this.el.getBorderWidth("rl");
38084             if(this.config.adjustments){
38085                 w += this.config.adjustments[0];
38086             }
38087         }
38088         if(h !== null && h > 0){
38089             this.el.setHeight(h);
38090             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38091             h -= this.el.getBorderWidth("tb");
38092             if(this.config.adjustments){
38093                 h += this.config.adjustments[1];
38094             }
38095             this.bodyEl.setHeight(h);
38096             if(this.tabs){
38097                 h = this.tabs.syncHeight(h);
38098             }
38099         }
38100         if(this.panelSize){
38101             w = w !== null ? w : this.panelSize.width;
38102             h = h !== null ? h : this.panelSize.height;
38103         }
38104         if(this.activePanel){
38105             var el = this.activePanel.getEl();
38106             w = w !== null ? w : el.getWidth();
38107             h = h !== null ? h : el.getHeight();
38108             this.panelSize = {width: w, height: h};
38109             this.activePanel.setSize(w, h);
38110         }
38111         if(Roo.isIE && this.tabs){
38112             this.tabs.el.repaint();
38113         }
38114     },
38115
38116     /**
38117      * Returns the container element for this region.
38118      * @return {Roo.Element}
38119      */
38120     getEl : function(){
38121         return this.el;
38122     },
38123
38124     /**
38125      * Hides this region.
38126      */
38127     hide : function(){
38128         //if(!this.collapsed){
38129             this.el.dom.style.left = "-2000px";
38130             this.el.hide();
38131         //}else{
38132          //   this.collapsedEl.dom.style.left = "-2000px";
38133          //   this.collapsedEl.hide();
38134        // }
38135         this.visible = false;
38136         this.fireEvent("visibilitychange", this, false);
38137     },
38138
38139     /**
38140      * Shows this region if it was previously hidden.
38141      */
38142     show : function(){
38143         //if(!this.collapsed){
38144             this.el.show();
38145         //}else{
38146         //    this.collapsedEl.show();
38147        // }
38148         this.visible = true;
38149         this.fireEvent("visibilitychange", this, true);
38150     },
38151 /*
38152     closeClicked : function(){
38153         if(this.activePanel){
38154             this.remove(this.activePanel);
38155         }
38156     },
38157
38158     collapseClick : function(e){
38159         if(this.isSlid){
38160            e.stopPropagation();
38161            this.slideIn();
38162         }else{
38163            e.stopPropagation();
38164            this.slideOut();
38165         }
38166     },
38167 */
38168     /**
38169      * Collapses this region.
38170      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38171      */
38172     /*
38173     collapse : function(skipAnim, skipCheck = false){
38174         if(this.collapsed) {
38175             return;
38176         }
38177         
38178         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38179             
38180             this.collapsed = true;
38181             if(this.split){
38182                 this.split.el.hide();
38183             }
38184             if(this.config.animate && skipAnim !== true){
38185                 this.fireEvent("invalidated", this);
38186                 this.animateCollapse();
38187             }else{
38188                 this.el.setLocation(-20000,-20000);
38189                 this.el.hide();
38190                 this.collapsedEl.show();
38191                 this.fireEvent("collapsed", this);
38192                 this.fireEvent("invalidated", this);
38193             }
38194         }
38195         
38196     },
38197 */
38198     animateCollapse : function(){
38199         // overridden
38200     },
38201
38202     /**
38203      * Expands this region if it was previously collapsed.
38204      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38205      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38206      */
38207     /*
38208     expand : function(e, skipAnim){
38209         if(e) {
38210             e.stopPropagation();
38211         }
38212         if(!this.collapsed || this.el.hasActiveFx()) {
38213             return;
38214         }
38215         if(this.isSlid){
38216             this.afterSlideIn();
38217             skipAnim = true;
38218         }
38219         this.collapsed = false;
38220         if(this.config.animate && skipAnim !== true){
38221             this.animateExpand();
38222         }else{
38223             this.el.show();
38224             if(this.split){
38225                 this.split.el.show();
38226             }
38227             this.collapsedEl.setLocation(-2000,-2000);
38228             this.collapsedEl.hide();
38229             this.fireEvent("invalidated", this);
38230             this.fireEvent("expanded", this);
38231         }
38232     },
38233 */
38234     animateExpand : function(){
38235         // overridden
38236     },
38237
38238     initTabs : function()
38239     {
38240         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38241         
38242         var ts = new Roo.bootstrap.panel.Tabs({
38243             el: this.bodyEl.dom,
38244             region : this,
38245             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38246             disableTooltips: this.config.disableTabTips,
38247             toolbar : this.config.toolbar
38248         });
38249         
38250         if(this.config.hideTabs){
38251             ts.stripWrap.setDisplayed(false);
38252         }
38253         this.tabs = ts;
38254         ts.resizeTabs = this.config.resizeTabs === true;
38255         ts.minTabWidth = this.config.minTabWidth || 40;
38256         ts.maxTabWidth = this.config.maxTabWidth || 250;
38257         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38258         ts.monitorResize = false;
38259         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38260         ts.bodyEl.addClass('roo-layout-tabs-body');
38261         this.panels.each(this.initPanelAsTab, this);
38262     },
38263
38264     initPanelAsTab : function(panel){
38265         var ti = this.tabs.addTab(
38266             panel.getEl().id,
38267             panel.getTitle(),
38268             null,
38269             this.config.closeOnTab && panel.isClosable(),
38270             panel.tpl
38271         );
38272         if(panel.tabTip !== undefined){
38273             ti.setTooltip(panel.tabTip);
38274         }
38275         ti.on("activate", function(){
38276               this.setActivePanel(panel);
38277         }, this);
38278         
38279         if(this.config.closeOnTab){
38280             ti.on("beforeclose", function(t, e){
38281                 e.cancel = true;
38282                 this.remove(panel);
38283             }, this);
38284         }
38285         
38286         panel.tabItem = ti;
38287         
38288         return ti;
38289     },
38290
38291     updatePanelTitle : function(panel, title)
38292     {
38293         if(this.activePanel == panel){
38294             this.updateTitle(title);
38295         }
38296         if(this.tabs){
38297             var ti = this.tabs.getTab(panel.getEl().id);
38298             ti.setText(title);
38299             if(panel.tabTip !== undefined){
38300                 ti.setTooltip(panel.tabTip);
38301             }
38302         }
38303     },
38304
38305     updateTitle : function(title){
38306         if(this.titleTextEl && !this.config.title){
38307             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38308         }
38309     },
38310
38311     setActivePanel : function(panel)
38312     {
38313         panel = this.getPanel(panel);
38314         if(this.activePanel && this.activePanel != panel){
38315             if(this.activePanel.setActiveState(false) === false){
38316                 return;
38317             }
38318         }
38319         this.activePanel = panel;
38320         panel.setActiveState(true);
38321         if(this.panelSize){
38322             panel.setSize(this.panelSize.width, this.panelSize.height);
38323         }
38324         if(this.closeBtn){
38325             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38326         }
38327         this.updateTitle(panel.getTitle());
38328         if(this.tabs){
38329             this.fireEvent("invalidated", this);
38330         }
38331         this.fireEvent("panelactivated", this, panel);
38332     },
38333
38334     /**
38335      * Shows the specified panel.
38336      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38337      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38338      */
38339     showPanel : function(panel)
38340     {
38341         panel = this.getPanel(panel);
38342         if(panel){
38343             if(this.tabs){
38344                 var tab = this.tabs.getTab(panel.getEl().id);
38345                 if(tab.isHidden()){
38346                     this.tabs.unhideTab(tab.id);
38347                 }
38348                 tab.activate();
38349             }else{
38350                 this.setActivePanel(panel);
38351             }
38352         }
38353         return panel;
38354     },
38355
38356     /**
38357      * Get the active panel for this region.
38358      * @return {Roo.ContentPanel} The active panel or null
38359      */
38360     getActivePanel : function(){
38361         return this.activePanel;
38362     },
38363
38364     validateVisibility : function(){
38365         if(this.panels.getCount() < 1){
38366             this.updateTitle("&#160;");
38367             this.closeBtn.hide();
38368             this.hide();
38369         }else{
38370             if(!this.isVisible()){
38371                 this.show();
38372             }
38373         }
38374     },
38375
38376     /**
38377      * Adds the passed ContentPanel(s) to this region.
38378      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38379      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38380      */
38381     add : function(panel)
38382     {
38383         if(arguments.length > 1){
38384             for(var i = 0, len = arguments.length; i < len; i++) {
38385                 this.add(arguments[i]);
38386             }
38387             return null;
38388         }
38389         
38390         // if we have not been rendered yet, then we can not really do much of this..
38391         if (!this.bodyEl) {
38392             this.unrendered_panels.push(panel);
38393             return panel;
38394         }
38395         
38396         
38397         
38398         
38399         if(this.hasPanel(panel)){
38400             this.showPanel(panel);
38401             return panel;
38402         }
38403         panel.setRegion(this);
38404         this.panels.add(panel);
38405        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38406             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38407             // and hide them... ???
38408             this.bodyEl.dom.appendChild(panel.getEl().dom);
38409             if(panel.background !== true){
38410                 this.setActivePanel(panel);
38411             }
38412             this.fireEvent("paneladded", this, panel);
38413             return panel;
38414         }
38415         */
38416         if(!this.tabs){
38417             this.initTabs();
38418         }else{
38419             this.initPanelAsTab(panel);
38420         }
38421         
38422         
38423         if(panel.background !== true){
38424             this.tabs.activate(panel.getEl().id);
38425         }
38426         this.fireEvent("paneladded", this, panel);
38427         return panel;
38428     },
38429
38430     /**
38431      * Hides the tab for the specified panel.
38432      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38433      */
38434     hidePanel : function(panel){
38435         if(this.tabs && (panel = this.getPanel(panel))){
38436             this.tabs.hideTab(panel.getEl().id);
38437         }
38438     },
38439
38440     /**
38441      * Unhides the tab for a previously hidden panel.
38442      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38443      */
38444     unhidePanel : function(panel){
38445         if(this.tabs && (panel = this.getPanel(panel))){
38446             this.tabs.unhideTab(panel.getEl().id);
38447         }
38448     },
38449
38450     clearPanels : function(){
38451         while(this.panels.getCount() > 0){
38452              this.remove(this.panels.first());
38453         }
38454     },
38455
38456     /**
38457      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38458      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38459      * @param {Boolean} preservePanel Overrides the config preservePanel option
38460      * @return {Roo.ContentPanel} The panel that was removed
38461      */
38462     remove : function(panel, preservePanel)
38463     {
38464         panel = this.getPanel(panel);
38465         if(!panel){
38466             return null;
38467         }
38468         var e = {};
38469         this.fireEvent("beforeremove", this, panel, e);
38470         if(e.cancel === true){
38471             return null;
38472         }
38473         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38474         var panelId = panel.getId();
38475         this.panels.removeKey(panelId);
38476         if(preservePanel){
38477             document.body.appendChild(panel.getEl().dom);
38478         }
38479         if(this.tabs){
38480             this.tabs.removeTab(panel.getEl().id);
38481         }else if (!preservePanel){
38482             this.bodyEl.dom.removeChild(panel.getEl().dom);
38483         }
38484         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38485             var p = this.panels.first();
38486             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38487             tempEl.appendChild(p.getEl().dom);
38488             this.bodyEl.update("");
38489             this.bodyEl.dom.appendChild(p.getEl().dom);
38490             tempEl = null;
38491             this.updateTitle(p.getTitle());
38492             this.tabs = null;
38493             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38494             this.setActivePanel(p);
38495         }
38496         panel.setRegion(null);
38497         if(this.activePanel == panel){
38498             this.activePanel = null;
38499         }
38500         if(this.config.autoDestroy !== false && preservePanel !== true){
38501             try{panel.destroy();}catch(e){}
38502         }
38503         this.fireEvent("panelremoved", this, panel);
38504         return panel;
38505     },
38506
38507     /**
38508      * Returns the TabPanel component used by this region
38509      * @return {Roo.TabPanel}
38510      */
38511     getTabs : function(){
38512         return this.tabs;
38513     },
38514
38515     createTool : function(parentEl, className){
38516         var btn = Roo.DomHelper.append(parentEl, {
38517             tag: "div",
38518             cls: "x-layout-tools-button",
38519             children: [ {
38520                 tag: "div",
38521                 cls: "roo-layout-tools-button-inner " + className,
38522                 html: "&#160;"
38523             }]
38524         }, true);
38525         btn.addClassOnOver("roo-layout-tools-button-over");
38526         return btn;
38527     }
38528 });/*
38529  * Based on:
38530  * Ext JS Library 1.1.1
38531  * Copyright(c) 2006-2007, Ext JS, LLC.
38532  *
38533  * Originally Released Under LGPL - original licence link has changed is not relivant.
38534  *
38535  * Fork - LGPL
38536  * <script type="text/javascript">
38537  */
38538  
38539
38540
38541 /**
38542  * @class Roo.SplitLayoutRegion
38543  * @extends Roo.LayoutRegion
38544  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38545  */
38546 Roo.bootstrap.layout.Split = function(config){
38547     this.cursor = config.cursor;
38548     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38549 };
38550
38551 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38552 {
38553     splitTip : "Drag to resize.",
38554     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38555     useSplitTips : false,
38556
38557     applyConfig : function(config){
38558         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38559     },
38560     
38561     onRender : function(ctr,pos) {
38562         
38563         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38564         if(!this.config.split){
38565             return;
38566         }
38567         if(!this.split){
38568             
38569             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38570                             tag: "div",
38571                             id: this.el.id + "-split",
38572                             cls: "roo-layout-split roo-layout-split-"+this.position,
38573                             html: "&#160;"
38574             });
38575             /** The SplitBar for this region 
38576             * @type Roo.SplitBar */
38577             // does not exist yet...
38578             Roo.log([this.position, this.orientation]);
38579             
38580             this.split = new Roo.bootstrap.SplitBar({
38581                 dragElement : splitEl,
38582                 resizingElement: this.el,
38583                 orientation : this.orientation
38584             });
38585             
38586             this.split.on("moved", this.onSplitMove, this);
38587             this.split.useShim = this.config.useShim === true;
38588             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38589             if(this.useSplitTips){
38590                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38591             }
38592             //if(config.collapsible){
38593             //    this.split.el.on("dblclick", this.collapse,  this);
38594             //}
38595         }
38596         if(typeof this.config.minSize != "undefined"){
38597             this.split.minSize = this.config.minSize;
38598         }
38599         if(typeof this.config.maxSize != "undefined"){
38600             this.split.maxSize = this.config.maxSize;
38601         }
38602         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38603             this.hideSplitter();
38604         }
38605         
38606     },
38607
38608     getHMaxSize : function(){
38609          var cmax = this.config.maxSize || 10000;
38610          var center = this.mgr.getRegion("center");
38611          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38612     },
38613
38614     getVMaxSize : function(){
38615          var cmax = this.config.maxSize || 10000;
38616          var center = this.mgr.getRegion("center");
38617          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38618     },
38619
38620     onSplitMove : function(split, newSize){
38621         this.fireEvent("resized", this, newSize);
38622     },
38623     
38624     /** 
38625      * Returns the {@link Roo.SplitBar} for this region.
38626      * @return {Roo.SplitBar}
38627      */
38628     getSplitBar : function(){
38629         return this.split;
38630     },
38631     
38632     hide : function(){
38633         this.hideSplitter();
38634         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38635     },
38636
38637     hideSplitter : function(){
38638         if(this.split){
38639             this.split.el.setLocation(-2000,-2000);
38640             this.split.el.hide();
38641         }
38642     },
38643
38644     show : function(){
38645         if(this.split){
38646             this.split.el.show();
38647         }
38648         Roo.bootstrap.layout.Split.superclass.show.call(this);
38649     },
38650     
38651     beforeSlide: function(){
38652         if(Roo.isGecko){// firefox overflow auto bug workaround
38653             this.bodyEl.clip();
38654             if(this.tabs) {
38655                 this.tabs.bodyEl.clip();
38656             }
38657             if(this.activePanel){
38658                 this.activePanel.getEl().clip();
38659                 
38660                 if(this.activePanel.beforeSlide){
38661                     this.activePanel.beforeSlide();
38662                 }
38663             }
38664         }
38665     },
38666     
38667     afterSlide : function(){
38668         if(Roo.isGecko){// firefox overflow auto bug workaround
38669             this.bodyEl.unclip();
38670             if(this.tabs) {
38671                 this.tabs.bodyEl.unclip();
38672             }
38673             if(this.activePanel){
38674                 this.activePanel.getEl().unclip();
38675                 if(this.activePanel.afterSlide){
38676                     this.activePanel.afterSlide();
38677                 }
38678             }
38679         }
38680     },
38681
38682     initAutoHide : function(){
38683         if(this.autoHide !== false){
38684             if(!this.autoHideHd){
38685                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38686                 this.autoHideHd = {
38687                     "mouseout": function(e){
38688                         if(!e.within(this.el, true)){
38689                             st.delay(500);
38690                         }
38691                     },
38692                     "mouseover" : function(e){
38693                         st.cancel();
38694                     },
38695                     scope : this
38696                 };
38697             }
38698             this.el.on(this.autoHideHd);
38699         }
38700     },
38701
38702     clearAutoHide : function(){
38703         if(this.autoHide !== false){
38704             this.el.un("mouseout", this.autoHideHd.mouseout);
38705             this.el.un("mouseover", this.autoHideHd.mouseover);
38706         }
38707     },
38708
38709     clearMonitor : function(){
38710         Roo.get(document).un("click", this.slideInIf, this);
38711     },
38712
38713     // these names are backwards but not changed for compat
38714     slideOut : function(){
38715         if(this.isSlid || this.el.hasActiveFx()){
38716             return;
38717         }
38718         this.isSlid = true;
38719         if(this.collapseBtn){
38720             this.collapseBtn.hide();
38721         }
38722         this.closeBtnState = this.closeBtn.getStyle('display');
38723         this.closeBtn.hide();
38724         if(this.stickBtn){
38725             this.stickBtn.show();
38726         }
38727         this.el.show();
38728         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38729         this.beforeSlide();
38730         this.el.setStyle("z-index", 10001);
38731         this.el.slideIn(this.getSlideAnchor(), {
38732             callback: function(){
38733                 this.afterSlide();
38734                 this.initAutoHide();
38735                 Roo.get(document).on("click", this.slideInIf, this);
38736                 this.fireEvent("slideshow", this);
38737             },
38738             scope: this,
38739             block: true
38740         });
38741     },
38742
38743     afterSlideIn : function(){
38744         this.clearAutoHide();
38745         this.isSlid = false;
38746         this.clearMonitor();
38747         this.el.setStyle("z-index", "");
38748         if(this.collapseBtn){
38749             this.collapseBtn.show();
38750         }
38751         this.closeBtn.setStyle('display', this.closeBtnState);
38752         if(this.stickBtn){
38753             this.stickBtn.hide();
38754         }
38755         this.fireEvent("slidehide", this);
38756     },
38757
38758     slideIn : function(cb){
38759         if(!this.isSlid || this.el.hasActiveFx()){
38760             Roo.callback(cb);
38761             return;
38762         }
38763         this.isSlid = false;
38764         this.beforeSlide();
38765         this.el.slideOut(this.getSlideAnchor(), {
38766             callback: function(){
38767                 this.el.setLeftTop(-10000, -10000);
38768                 this.afterSlide();
38769                 this.afterSlideIn();
38770                 Roo.callback(cb);
38771             },
38772             scope: this,
38773             block: true
38774         });
38775     },
38776     
38777     slideInIf : function(e){
38778         if(!e.within(this.el)){
38779             this.slideIn();
38780         }
38781     },
38782
38783     animateCollapse : function(){
38784         this.beforeSlide();
38785         this.el.setStyle("z-index", 20000);
38786         var anchor = this.getSlideAnchor();
38787         this.el.slideOut(anchor, {
38788             callback : function(){
38789                 this.el.setStyle("z-index", "");
38790                 this.collapsedEl.slideIn(anchor, {duration:.3});
38791                 this.afterSlide();
38792                 this.el.setLocation(-10000,-10000);
38793                 this.el.hide();
38794                 this.fireEvent("collapsed", this);
38795             },
38796             scope: this,
38797             block: true
38798         });
38799     },
38800
38801     animateExpand : function(){
38802         this.beforeSlide();
38803         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38804         this.el.setStyle("z-index", 20000);
38805         this.collapsedEl.hide({
38806             duration:.1
38807         });
38808         this.el.slideIn(this.getSlideAnchor(), {
38809             callback : function(){
38810                 this.el.setStyle("z-index", "");
38811                 this.afterSlide();
38812                 if(this.split){
38813                     this.split.el.show();
38814                 }
38815                 this.fireEvent("invalidated", this);
38816                 this.fireEvent("expanded", this);
38817             },
38818             scope: this,
38819             block: true
38820         });
38821     },
38822
38823     anchors : {
38824         "west" : "left",
38825         "east" : "right",
38826         "north" : "top",
38827         "south" : "bottom"
38828     },
38829
38830     sanchors : {
38831         "west" : "l",
38832         "east" : "r",
38833         "north" : "t",
38834         "south" : "b"
38835     },
38836
38837     canchors : {
38838         "west" : "tl-tr",
38839         "east" : "tr-tl",
38840         "north" : "tl-bl",
38841         "south" : "bl-tl"
38842     },
38843
38844     getAnchor : function(){
38845         return this.anchors[this.position];
38846     },
38847
38848     getCollapseAnchor : function(){
38849         return this.canchors[this.position];
38850     },
38851
38852     getSlideAnchor : function(){
38853         return this.sanchors[this.position];
38854     },
38855
38856     getAlignAdj : function(){
38857         var cm = this.cmargins;
38858         switch(this.position){
38859             case "west":
38860                 return [0, 0];
38861             break;
38862             case "east":
38863                 return [0, 0];
38864             break;
38865             case "north":
38866                 return [0, 0];
38867             break;
38868             case "south":
38869                 return [0, 0];
38870             break;
38871         }
38872     },
38873
38874     getExpandAdj : function(){
38875         var c = this.collapsedEl, cm = this.cmargins;
38876         switch(this.position){
38877             case "west":
38878                 return [-(cm.right+c.getWidth()+cm.left), 0];
38879             break;
38880             case "east":
38881                 return [cm.right+c.getWidth()+cm.left, 0];
38882             break;
38883             case "north":
38884                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38885             break;
38886             case "south":
38887                 return [0, cm.top+cm.bottom+c.getHeight()];
38888             break;
38889         }
38890     }
38891 });/*
38892  * Based on:
38893  * Ext JS Library 1.1.1
38894  * Copyright(c) 2006-2007, Ext JS, LLC.
38895  *
38896  * Originally Released Under LGPL - original licence link has changed is not relivant.
38897  *
38898  * Fork - LGPL
38899  * <script type="text/javascript">
38900  */
38901 /*
38902  * These classes are private internal classes
38903  */
38904 Roo.bootstrap.layout.Center = function(config){
38905     config.region = "center";
38906     Roo.bootstrap.layout.Region.call(this, config);
38907     this.visible = true;
38908     this.minWidth = config.minWidth || 20;
38909     this.minHeight = config.minHeight || 20;
38910 };
38911
38912 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38913     hide : function(){
38914         // center panel can't be hidden
38915     },
38916     
38917     show : function(){
38918         // center panel can't be hidden
38919     },
38920     
38921     getMinWidth: function(){
38922         return this.minWidth;
38923     },
38924     
38925     getMinHeight: function(){
38926         return this.minHeight;
38927     }
38928 });
38929
38930
38931
38932
38933  
38934
38935
38936
38937
38938
38939
38940 Roo.bootstrap.layout.North = function(config)
38941 {
38942     config.region = 'north';
38943     config.cursor = 'n-resize';
38944     
38945     Roo.bootstrap.layout.Split.call(this, config);
38946     
38947     
38948     if(this.split){
38949         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38950         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38951         this.split.el.addClass("roo-layout-split-v");
38952     }
38953     var size = config.initialSize || config.height;
38954     if(typeof size != "undefined"){
38955         this.el.setHeight(size);
38956     }
38957 };
38958 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38959 {
38960     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38961     
38962     
38963     
38964     getBox : function(){
38965         if(this.collapsed){
38966             return this.collapsedEl.getBox();
38967         }
38968         var box = this.el.getBox();
38969         if(this.split){
38970             box.height += this.split.el.getHeight();
38971         }
38972         return box;
38973     },
38974     
38975     updateBox : function(box){
38976         if(this.split && !this.collapsed){
38977             box.height -= this.split.el.getHeight();
38978             this.split.el.setLeft(box.x);
38979             this.split.el.setTop(box.y+box.height);
38980             this.split.el.setWidth(box.width);
38981         }
38982         if(this.collapsed){
38983             this.updateBody(box.width, null);
38984         }
38985         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38986     }
38987 });
38988
38989
38990
38991
38992
38993 Roo.bootstrap.layout.South = function(config){
38994     config.region = 'south';
38995     config.cursor = 's-resize';
38996     Roo.bootstrap.layout.Split.call(this, config);
38997     if(this.split){
38998         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38999         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39000         this.split.el.addClass("roo-layout-split-v");
39001     }
39002     var size = config.initialSize || config.height;
39003     if(typeof size != "undefined"){
39004         this.el.setHeight(size);
39005     }
39006 };
39007
39008 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39009     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39010     getBox : function(){
39011         if(this.collapsed){
39012             return this.collapsedEl.getBox();
39013         }
39014         var box = this.el.getBox();
39015         if(this.split){
39016             var sh = this.split.el.getHeight();
39017             box.height += sh;
39018             box.y -= sh;
39019         }
39020         return box;
39021     },
39022     
39023     updateBox : function(box){
39024         if(this.split && !this.collapsed){
39025             var sh = this.split.el.getHeight();
39026             box.height -= sh;
39027             box.y += sh;
39028             this.split.el.setLeft(box.x);
39029             this.split.el.setTop(box.y-sh);
39030             this.split.el.setWidth(box.width);
39031         }
39032         if(this.collapsed){
39033             this.updateBody(box.width, null);
39034         }
39035         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39036     }
39037 });
39038
39039 Roo.bootstrap.layout.East = function(config){
39040     config.region = "east";
39041     config.cursor = "e-resize";
39042     Roo.bootstrap.layout.Split.call(this, config);
39043     if(this.split){
39044         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39045         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39046         this.split.el.addClass("roo-layout-split-h");
39047     }
39048     var size = config.initialSize || config.width;
39049     if(typeof size != "undefined"){
39050         this.el.setWidth(size);
39051     }
39052 };
39053 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39054     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39055     getBox : function(){
39056         if(this.collapsed){
39057             return this.collapsedEl.getBox();
39058         }
39059         var box = this.el.getBox();
39060         if(this.split){
39061             var sw = this.split.el.getWidth();
39062             box.width += sw;
39063             box.x -= sw;
39064         }
39065         return box;
39066     },
39067
39068     updateBox : function(box){
39069         if(this.split && !this.collapsed){
39070             var sw = this.split.el.getWidth();
39071             box.width -= sw;
39072             this.split.el.setLeft(box.x);
39073             this.split.el.setTop(box.y);
39074             this.split.el.setHeight(box.height);
39075             box.x += sw;
39076         }
39077         if(this.collapsed){
39078             this.updateBody(null, box.height);
39079         }
39080         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39081     }
39082 });
39083
39084 Roo.bootstrap.layout.West = function(config){
39085     config.region = "west";
39086     config.cursor = "w-resize";
39087     
39088     Roo.bootstrap.layout.Split.call(this, config);
39089     if(this.split){
39090         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39091         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39092         this.split.el.addClass("roo-layout-split-h");
39093     }
39094     
39095 };
39096 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39097     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39098     
39099     onRender: function(ctr, pos)
39100     {
39101         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39102         var size = this.config.initialSize || this.config.width;
39103         if(typeof size != "undefined"){
39104             this.el.setWidth(size);
39105         }
39106     },
39107     
39108     getBox : function(){
39109         if(this.collapsed){
39110             return this.collapsedEl.getBox();
39111         }
39112         var box = this.el.getBox();
39113         if(this.split){
39114             box.width += this.split.el.getWidth();
39115         }
39116         return box;
39117     },
39118     
39119     updateBox : function(box){
39120         if(this.split && !this.collapsed){
39121             var sw = this.split.el.getWidth();
39122             box.width -= sw;
39123             this.split.el.setLeft(box.x+box.width);
39124             this.split.el.setTop(box.y);
39125             this.split.el.setHeight(box.height);
39126         }
39127         if(this.collapsed){
39128             this.updateBody(null, box.height);
39129         }
39130         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39131     }
39132 });Roo.namespace("Roo.bootstrap.panel");/*
39133  * Based on:
39134  * Ext JS Library 1.1.1
39135  * Copyright(c) 2006-2007, Ext JS, LLC.
39136  *
39137  * Originally Released Under LGPL - original licence link has changed is not relivant.
39138  *
39139  * Fork - LGPL
39140  * <script type="text/javascript">
39141  */
39142 /**
39143  * @class Roo.ContentPanel
39144  * @extends Roo.util.Observable
39145  * A basic ContentPanel element.
39146  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39147  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39148  * @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
39149  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39150  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39151  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39152  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39153  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39154  * @cfg {String} title          The title for this panel
39155  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39156  * @cfg {String} url            Calls {@link #setUrl} with this value
39157  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39158  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39159  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39160  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39161  * @cfg {Boolean} badges render the badges
39162  * @cfg {String} cls  extra classes to use  
39163  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39164
39165  * @constructor
39166  * Create a new ContentPanel.
39167  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39168  * @param {String/Object} config A string to set only the title or a config object
39169  * @param {String} content (optional) Set the HTML content for this panel
39170  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39171  */
39172 Roo.bootstrap.panel.Content = function( config){
39173     
39174     this.tpl = config.tpl || false;
39175     
39176     var el = config.el;
39177     var content = config.content;
39178
39179     if(config.autoCreate){ // xtype is available if this is called from factory
39180         el = Roo.id();
39181     }
39182     this.el = Roo.get(el);
39183     if(!this.el && config && config.autoCreate){
39184         if(typeof config.autoCreate == "object"){
39185             if(!config.autoCreate.id){
39186                 config.autoCreate.id = config.id||el;
39187             }
39188             this.el = Roo.DomHelper.append(document.body,
39189                         config.autoCreate, true);
39190         }else{
39191             var elcfg =  {
39192                 tag: "div",
39193                 cls: (config.cls || '') +
39194                     (config.background ? ' bg-' + config.background : '') +
39195                     " roo-layout-inactive-content",
39196                 id: config.id||el
39197             };
39198             if (config.html) {
39199                 elcfg.html = config.html;
39200                 
39201             }
39202                         
39203             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39204         }
39205     } 
39206     this.closable = false;
39207     this.loaded = false;
39208     this.active = false;
39209    
39210       
39211     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39212         
39213         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39214         
39215         this.wrapEl = this.el; //this.el.wrap();
39216         var ti = [];
39217         if (config.toolbar.items) {
39218             ti = config.toolbar.items ;
39219             delete config.toolbar.items ;
39220         }
39221         
39222         var nitems = [];
39223         this.toolbar.render(this.wrapEl, 'before');
39224         for(var i =0;i < ti.length;i++) {
39225           //  Roo.log(['add child', items[i]]);
39226             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39227         }
39228         this.toolbar.items = nitems;
39229         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39230         delete config.toolbar;
39231         
39232     }
39233     /*
39234     // xtype created footer. - not sure if will work as we normally have to render first..
39235     if (this.footer && !this.footer.el && this.footer.xtype) {
39236         if (!this.wrapEl) {
39237             this.wrapEl = this.el.wrap();
39238         }
39239     
39240         this.footer.container = this.wrapEl.createChild();
39241          
39242         this.footer = Roo.factory(this.footer, Roo);
39243         
39244     }
39245     */
39246     
39247      if(typeof config == "string"){
39248         this.title = config;
39249     }else{
39250         Roo.apply(this, config);
39251     }
39252     
39253     if(this.resizeEl){
39254         this.resizeEl = Roo.get(this.resizeEl, true);
39255     }else{
39256         this.resizeEl = this.el;
39257     }
39258     // handle view.xtype
39259     
39260  
39261     
39262     
39263     this.addEvents({
39264         /**
39265          * @event activate
39266          * Fires when this panel is activated. 
39267          * @param {Roo.ContentPanel} this
39268          */
39269         "activate" : true,
39270         /**
39271          * @event deactivate
39272          * Fires when this panel is activated. 
39273          * @param {Roo.ContentPanel} this
39274          */
39275         "deactivate" : true,
39276
39277         /**
39278          * @event resize
39279          * Fires when this panel is resized if fitToFrame is true.
39280          * @param {Roo.ContentPanel} this
39281          * @param {Number} width The width after any component adjustments
39282          * @param {Number} height The height after any component adjustments
39283          */
39284         "resize" : true,
39285         
39286          /**
39287          * @event render
39288          * Fires when this tab is created
39289          * @param {Roo.ContentPanel} this
39290          */
39291         "render" : true
39292         
39293         
39294         
39295     });
39296     
39297
39298     
39299     
39300     if(this.autoScroll){
39301         this.resizeEl.setStyle("overflow", "auto");
39302     } else {
39303         // fix randome scrolling
39304         //this.el.on('scroll', function() {
39305         //    Roo.log('fix random scolling');
39306         //    this.scrollTo('top',0); 
39307         //});
39308     }
39309     content = content || this.content;
39310     if(content){
39311         this.setContent(content);
39312     }
39313     if(config && config.url){
39314         this.setUrl(this.url, this.params, this.loadOnce);
39315     }
39316     
39317     
39318     
39319     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39320     
39321     if (this.view && typeof(this.view.xtype) != 'undefined') {
39322         this.view.el = this.el.appendChild(document.createElement("div"));
39323         this.view = Roo.factory(this.view); 
39324         this.view.render  &&  this.view.render(false, '');  
39325     }
39326     
39327     
39328     this.fireEvent('render', this);
39329 };
39330
39331 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39332     
39333     cls : '',
39334     background : '',
39335     
39336     tabTip : '',
39337     
39338     setRegion : function(region){
39339         this.region = region;
39340         this.setActiveClass(region && !this.background);
39341     },
39342     
39343     
39344     setActiveClass: function(state)
39345     {
39346         if(state){
39347            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39348            this.el.setStyle('position','relative');
39349         }else{
39350            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39351            this.el.setStyle('position', 'absolute');
39352         } 
39353     },
39354     
39355     /**
39356      * Returns the toolbar for this Panel if one was configured. 
39357      * @return {Roo.Toolbar} 
39358      */
39359     getToolbar : function(){
39360         return this.toolbar;
39361     },
39362     
39363     setActiveState : function(active)
39364     {
39365         this.active = active;
39366         this.setActiveClass(active);
39367         if(!active){
39368             if(this.fireEvent("deactivate", this) === false){
39369                 return false;
39370             }
39371             return true;
39372         }
39373         this.fireEvent("activate", this);
39374         return true;
39375     },
39376     /**
39377      * Updates this panel's element
39378      * @param {String} content The new content
39379      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39380     */
39381     setContent : function(content, loadScripts){
39382         this.el.update(content, loadScripts);
39383     },
39384
39385     ignoreResize : function(w, h){
39386         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39387             return true;
39388         }else{
39389             this.lastSize = {width: w, height: h};
39390             return false;
39391         }
39392     },
39393     /**
39394      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39395      * @return {Roo.UpdateManager} The UpdateManager
39396      */
39397     getUpdateManager : function(){
39398         return this.el.getUpdateManager();
39399     },
39400      /**
39401      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39402      * @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:
39403 <pre><code>
39404 panel.load({
39405     url: "your-url.php",
39406     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39407     callback: yourFunction,
39408     scope: yourObject, //(optional scope)
39409     discardUrl: false,
39410     nocache: false,
39411     text: "Loading...",
39412     timeout: 30,
39413     scripts: false
39414 });
39415 </code></pre>
39416      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39417      * 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.
39418      * @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}
39419      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39420      * @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.
39421      * @return {Roo.ContentPanel} this
39422      */
39423     load : function(){
39424         var um = this.el.getUpdateManager();
39425         um.update.apply(um, arguments);
39426         return this;
39427     },
39428
39429
39430     /**
39431      * 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.
39432      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39433      * @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)
39434      * @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)
39435      * @return {Roo.UpdateManager} The UpdateManager
39436      */
39437     setUrl : function(url, params, loadOnce){
39438         if(this.refreshDelegate){
39439             this.removeListener("activate", this.refreshDelegate);
39440         }
39441         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39442         this.on("activate", this.refreshDelegate);
39443         return this.el.getUpdateManager();
39444     },
39445     
39446     _handleRefresh : function(url, params, loadOnce){
39447         if(!loadOnce || !this.loaded){
39448             var updater = this.el.getUpdateManager();
39449             updater.update(url, params, this._setLoaded.createDelegate(this));
39450         }
39451     },
39452     
39453     _setLoaded : function(){
39454         this.loaded = true;
39455     }, 
39456     
39457     /**
39458      * Returns this panel's id
39459      * @return {String} 
39460      */
39461     getId : function(){
39462         return this.el.id;
39463     },
39464     
39465     /** 
39466      * Returns this panel's element - used by regiosn to add.
39467      * @return {Roo.Element} 
39468      */
39469     getEl : function(){
39470         return this.wrapEl || this.el;
39471     },
39472     
39473    
39474     
39475     adjustForComponents : function(width, height)
39476     {
39477         //Roo.log('adjustForComponents ');
39478         if(this.resizeEl != this.el){
39479             width -= this.el.getFrameWidth('lr');
39480             height -= this.el.getFrameWidth('tb');
39481         }
39482         if(this.toolbar){
39483             var te = this.toolbar.getEl();
39484             te.setWidth(width);
39485             height -= te.getHeight();
39486         }
39487         if(this.footer){
39488             var te = this.footer.getEl();
39489             te.setWidth(width);
39490             height -= te.getHeight();
39491         }
39492         
39493         
39494         if(this.adjustments){
39495             width += this.adjustments[0];
39496             height += this.adjustments[1];
39497         }
39498         return {"width": width, "height": height};
39499     },
39500     
39501     setSize : function(width, height){
39502         if(this.fitToFrame && !this.ignoreResize(width, height)){
39503             if(this.fitContainer && this.resizeEl != this.el){
39504                 this.el.setSize(width, height);
39505             }
39506             var size = this.adjustForComponents(width, height);
39507             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39508             this.fireEvent('resize', this, size.width, size.height);
39509         }
39510     },
39511     
39512     /**
39513      * Returns this panel's title
39514      * @return {String} 
39515      */
39516     getTitle : function(){
39517         
39518         if (typeof(this.title) != 'object') {
39519             return this.title;
39520         }
39521         
39522         var t = '';
39523         for (var k in this.title) {
39524             if (!this.title.hasOwnProperty(k)) {
39525                 continue;
39526             }
39527             
39528             if (k.indexOf('-') >= 0) {
39529                 var s = k.split('-');
39530                 for (var i = 0; i<s.length; i++) {
39531                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39532                 }
39533             } else {
39534                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39535             }
39536         }
39537         return t;
39538     },
39539     
39540     /**
39541      * Set this panel's title
39542      * @param {String} title
39543      */
39544     setTitle : function(title){
39545         this.title = title;
39546         if(this.region){
39547             this.region.updatePanelTitle(this, title);
39548         }
39549     },
39550     
39551     /**
39552      * Returns true is this panel was configured to be closable
39553      * @return {Boolean} 
39554      */
39555     isClosable : function(){
39556         return this.closable;
39557     },
39558     
39559     beforeSlide : function(){
39560         this.el.clip();
39561         this.resizeEl.clip();
39562     },
39563     
39564     afterSlide : function(){
39565         this.el.unclip();
39566         this.resizeEl.unclip();
39567     },
39568     
39569     /**
39570      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39571      *   Will fail silently if the {@link #setUrl} method has not been called.
39572      *   This does not activate the panel, just updates its content.
39573      */
39574     refresh : function(){
39575         if(this.refreshDelegate){
39576            this.loaded = false;
39577            this.refreshDelegate();
39578         }
39579     },
39580     
39581     /**
39582      * Destroys this panel
39583      */
39584     destroy : function(){
39585         this.el.removeAllListeners();
39586         var tempEl = document.createElement("span");
39587         tempEl.appendChild(this.el.dom);
39588         tempEl.innerHTML = "";
39589         this.el.remove();
39590         this.el = null;
39591     },
39592     
39593     /**
39594      * form - if the content panel contains a form - this is a reference to it.
39595      * @type {Roo.form.Form}
39596      */
39597     form : false,
39598     /**
39599      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39600      *    This contains a reference to it.
39601      * @type {Roo.View}
39602      */
39603     view : false,
39604     
39605       /**
39606      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39607      * <pre><code>
39608
39609 layout.addxtype({
39610        xtype : 'Form',
39611        items: [ .... ]
39612    }
39613 );
39614
39615 </code></pre>
39616      * @param {Object} cfg Xtype definition of item to add.
39617      */
39618     
39619     
39620     getChildContainer: function () {
39621         return this.getEl();
39622     }
39623     
39624     
39625     /*
39626         var  ret = new Roo.factory(cfg);
39627         return ret;
39628         
39629         
39630         // add form..
39631         if (cfg.xtype.match(/^Form$/)) {
39632             
39633             var el;
39634             //if (this.footer) {
39635             //    el = this.footer.container.insertSibling(false, 'before');
39636             //} else {
39637                 el = this.el.createChild();
39638             //}
39639
39640             this.form = new  Roo.form.Form(cfg);
39641             
39642             
39643             if ( this.form.allItems.length) {
39644                 this.form.render(el.dom);
39645             }
39646             return this.form;
39647         }
39648         // should only have one of theses..
39649         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39650             // views.. should not be just added - used named prop 'view''
39651             
39652             cfg.el = this.el.appendChild(document.createElement("div"));
39653             // factory?
39654             
39655             var ret = new Roo.factory(cfg);
39656              
39657              ret.render && ret.render(false, ''); // render blank..
39658             this.view = ret;
39659             return ret;
39660         }
39661         return false;
39662     }
39663     \*/
39664 });
39665  
39666 /**
39667  * @class Roo.bootstrap.panel.Grid
39668  * @extends Roo.bootstrap.panel.Content
39669  * @constructor
39670  * Create a new GridPanel.
39671  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39672  * @param {Object} config A the config object
39673   
39674  */
39675
39676
39677
39678 Roo.bootstrap.panel.Grid = function(config)
39679 {
39680     
39681       
39682     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39683         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39684
39685     config.el = this.wrapper;
39686     //this.el = this.wrapper;
39687     
39688       if (config.container) {
39689         // ctor'ed from a Border/panel.grid
39690         
39691         
39692         this.wrapper.setStyle("overflow", "hidden");
39693         this.wrapper.addClass('roo-grid-container');
39694
39695     }
39696     
39697     
39698     if(config.toolbar){
39699         var tool_el = this.wrapper.createChild();    
39700         this.toolbar = Roo.factory(config.toolbar);
39701         var ti = [];
39702         if (config.toolbar.items) {
39703             ti = config.toolbar.items ;
39704             delete config.toolbar.items ;
39705         }
39706         
39707         var nitems = [];
39708         this.toolbar.render(tool_el);
39709         for(var i =0;i < ti.length;i++) {
39710           //  Roo.log(['add child', items[i]]);
39711             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39712         }
39713         this.toolbar.items = nitems;
39714         
39715         delete config.toolbar;
39716     }
39717     
39718     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39719     config.grid.scrollBody = true;;
39720     config.grid.monitorWindowResize = false; // turn off autosizing
39721     config.grid.autoHeight = false;
39722     config.grid.autoWidth = false;
39723     
39724     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39725     
39726     if (config.background) {
39727         // render grid on panel activation (if panel background)
39728         this.on('activate', function(gp) {
39729             if (!gp.grid.rendered) {
39730                 gp.grid.render(this.wrapper);
39731                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39732             }
39733         });
39734             
39735     } else {
39736         this.grid.render(this.wrapper);
39737         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39738
39739     }
39740     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39741     // ??? needed ??? config.el = this.wrapper;
39742     
39743     
39744     
39745   
39746     // xtype created footer. - not sure if will work as we normally have to render first..
39747     if (this.footer && !this.footer.el && this.footer.xtype) {
39748         
39749         var ctr = this.grid.getView().getFooterPanel(true);
39750         this.footer.dataSource = this.grid.dataSource;
39751         this.footer = Roo.factory(this.footer, Roo);
39752         this.footer.render(ctr);
39753         
39754     }
39755     
39756     
39757     
39758     
39759      
39760 };
39761
39762 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39763     getId : function(){
39764         return this.grid.id;
39765     },
39766     
39767     /**
39768      * Returns the grid for this panel
39769      * @return {Roo.bootstrap.Table} 
39770      */
39771     getGrid : function(){
39772         return this.grid;    
39773     },
39774     
39775     setSize : function(width, height){
39776         if(!this.ignoreResize(width, height)){
39777             var grid = this.grid;
39778             var size = this.adjustForComponents(width, height);
39779             // tfoot is not a footer?
39780           
39781             
39782             var gridel = grid.getGridEl();
39783             gridel.setSize(size.width, size.height);
39784             
39785             var tbd = grid.getGridEl().select('tbody', true).first();
39786             var thd = grid.getGridEl().select('thead',true).first();
39787             var tbf= grid.getGridEl().select('tfoot', true).first();
39788
39789             if (tbf) {
39790                 size.height -= thd.getHeight();
39791             }
39792             if (thd) {
39793                 size.height -= thd.getHeight();
39794             }
39795             
39796             tbd.setSize(size.width, size.height );
39797             // this is for the account management tab -seems to work there.
39798             var thd = grid.getGridEl().select('thead',true).first();
39799             //if (tbd) {
39800             //    tbd.setSize(size.width, size.height - thd.getHeight());
39801             //}
39802              
39803             grid.autoSize();
39804         }
39805     },
39806      
39807     
39808     
39809     beforeSlide : function(){
39810         this.grid.getView().scroller.clip();
39811     },
39812     
39813     afterSlide : function(){
39814         this.grid.getView().scroller.unclip();
39815     },
39816     
39817     destroy : function(){
39818         this.grid.destroy();
39819         delete this.grid;
39820         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39821     }
39822 });
39823
39824 /**
39825  * @class Roo.bootstrap.panel.Nest
39826  * @extends Roo.bootstrap.panel.Content
39827  * @constructor
39828  * Create a new Panel, that can contain a layout.Border.
39829  * 
39830  * 
39831  * @param {Roo.BorderLayout} layout The layout for this panel
39832  * @param {String/Object} config A string to set only the title or a config object
39833  */
39834 Roo.bootstrap.panel.Nest = function(config)
39835 {
39836     // construct with only one argument..
39837     /* FIXME - implement nicer consturctors
39838     if (layout.layout) {
39839         config = layout;
39840         layout = config.layout;
39841         delete config.layout;
39842     }
39843     if (layout.xtype && !layout.getEl) {
39844         // then layout needs constructing..
39845         layout = Roo.factory(layout, Roo);
39846     }
39847     */
39848     
39849     config.el =  config.layout.getEl();
39850     
39851     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39852     
39853     config.layout.monitorWindowResize = false; // turn off autosizing
39854     this.layout = config.layout;
39855     this.layout.getEl().addClass("roo-layout-nested-layout");
39856     this.layout.parent = this;
39857     
39858     
39859     
39860     
39861 };
39862
39863 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39864
39865     setSize : function(width, height){
39866         if(!this.ignoreResize(width, height)){
39867             var size = this.adjustForComponents(width, height);
39868             var el = this.layout.getEl();
39869             if (size.height < 1) {
39870                 el.setWidth(size.width);   
39871             } else {
39872                 el.setSize(size.width, size.height);
39873             }
39874             var touch = el.dom.offsetWidth;
39875             this.layout.layout();
39876             // ie requires a double layout on the first pass
39877             if(Roo.isIE && !this.initialized){
39878                 this.initialized = true;
39879                 this.layout.layout();
39880             }
39881         }
39882     },
39883     
39884     // activate all subpanels if not currently active..
39885     
39886     setActiveState : function(active){
39887         this.active = active;
39888         this.setActiveClass(active);
39889         
39890         if(!active){
39891             this.fireEvent("deactivate", this);
39892             return;
39893         }
39894         
39895         this.fireEvent("activate", this);
39896         // not sure if this should happen before or after..
39897         if (!this.layout) {
39898             return; // should not happen..
39899         }
39900         var reg = false;
39901         for (var r in this.layout.regions) {
39902             reg = this.layout.getRegion(r);
39903             if (reg.getActivePanel()) {
39904                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39905                 reg.setActivePanel(reg.getActivePanel());
39906                 continue;
39907             }
39908             if (!reg.panels.length) {
39909                 continue;
39910             }
39911             reg.showPanel(reg.getPanel(0));
39912         }
39913         
39914         
39915         
39916         
39917     },
39918     
39919     /**
39920      * Returns the nested BorderLayout for this panel
39921      * @return {Roo.BorderLayout} 
39922      */
39923     getLayout : function(){
39924         return this.layout;
39925     },
39926     
39927      /**
39928      * Adds a xtype elements to the layout of the nested panel
39929      * <pre><code>
39930
39931 panel.addxtype({
39932        xtype : 'ContentPanel',
39933        region: 'west',
39934        items: [ .... ]
39935    }
39936 );
39937
39938 panel.addxtype({
39939         xtype : 'NestedLayoutPanel',
39940         region: 'west',
39941         layout: {
39942            center: { },
39943            west: { }   
39944         },
39945         items : [ ... list of content panels or nested layout panels.. ]
39946    }
39947 );
39948 </code></pre>
39949      * @param {Object} cfg Xtype definition of item to add.
39950      */
39951     addxtype : function(cfg) {
39952         return this.layout.addxtype(cfg);
39953     
39954     }
39955 });/*
39956  * Based on:
39957  * Ext JS Library 1.1.1
39958  * Copyright(c) 2006-2007, Ext JS, LLC.
39959  *
39960  * Originally Released Under LGPL - original licence link has changed is not relivant.
39961  *
39962  * Fork - LGPL
39963  * <script type="text/javascript">
39964  */
39965 /**
39966  * @class Roo.TabPanel
39967  * @extends Roo.util.Observable
39968  * A lightweight tab container.
39969  * <br><br>
39970  * Usage:
39971  * <pre><code>
39972 // basic tabs 1, built from existing content
39973 var tabs = new Roo.TabPanel("tabs1");
39974 tabs.addTab("script", "View Script");
39975 tabs.addTab("markup", "View Markup");
39976 tabs.activate("script");
39977
39978 // more advanced tabs, built from javascript
39979 var jtabs = new Roo.TabPanel("jtabs");
39980 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39981
39982 // set up the UpdateManager
39983 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39984 var updater = tab2.getUpdateManager();
39985 updater.setDefaultUrl("ajax1.htm");
39986 tab2.on('activate', updater.refresh, updater, true);
39987
39988 // Use setUrl for Ajax loading
39989 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39990 tab3.setUrl("ajax2.htm", null, true);
39991
39992 // Disabled tab
39993 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39994 tab4.disable();
39995
39996 jtabs.activate("jtabs-1");
39997  * </code></pre>
39998  * @constructor
39999  * Create a new TabPanel.
40000  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40001  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40002  */
40003 Roo.bootstrap.panel.Tabs = function(config){
40004     /**
40005     * The container element for this TabPanel.
40006     * @type Roo.Element
40007     */
40008     this.el = Roo.get(config.el);
40009     delete config.el;
40010     if(config){
40011         if(typeof config == "boolean"){
40012             this.tabPosition = config ? "bottom" : "top";
40013         }else{
40014             Roo.apply(this, config);
40015         }
40016     }
40017     
40018     if(this.tabPosition == "bottom"){
40019         // if tabs are at the bottom = create the body first.
40020         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40021         this.el.addClass("roo-tabs-bottom");
40022     }
40023     // next create the tabs holders
40024     
40025     if (this.tabPosition == "west"){
40026         
40027         var reg = this.region; // fake it..
40028         while (reg) {
40029             if (!reg.mgr.parent) {
40030                 break;
40031             }
40032             reg = reg.mgr.parent.region;
40033         }
40034         Roo.log("got nest?");
40035         Roo.log(reg);
40036         if (reg.mgr.getRegion('west')) {
40037             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40038             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40039             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40040             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40041             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40042         
40043             
40044         }
40045         
40046         
40047     } else {
40048      
40049         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40050         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40051         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40052         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40053     }
40054     
40055     
40056     if(Roo.isIE){
40057         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40058     }
40059     
40060     // finally - if tabs are at the top, then create the body last..
40061     if(this.tabPosition != "bottom"){
40062         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40063          * @type Roo.Element
40064          */
40065         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40066         this.el.addClass("roo-tabs-top");
40067     }
40068     this.items = [];
40069
40070     this.bodyEl.setStyle("position", "relative");
40071
40072     this.active = null;
40073     this.activateDelegate = this.activate.createDelegate(this);
40074
40075     this.addEvents({
40076         /**
40077          * @event tabchange
40078          * Fires when the active tab changes
40079          * @param {Roo.TabPanel} this
40080          * @param {Roo.TabPanelItem} activePanel The new active tab
40081          */
40082         "tabchange": true,
40083         /**
40084          * @event beforetabchange
40085          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40086          * @param {Roo.TabPanel} this
40087          * @param {Object} e Set cancel to true on this object to cancel the tab change
40088          * @param {Roo.TabPanelItem} tab The tab being changed to
40089          */
40090         "beforetabchange" : true
40091     });
40092
40093     Roo.EventManager.onWindowResize(this.onResize, this);
40094     this.cpad = this.el.getPadding("lr");
40095     this.hiddenCount = 0;
40096
40097
40098     // toolbar on the tabbar support...
40099     if (this.toolbar) {
40100         alert("no toolbar support yet");
40101         this.toolbar  = false;
40102         /*
40103         var tcfg = this.toolbar;
40104         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40105         this.toolbar = new Roo.Toolbar(tcfg);
40106         if (Roo.isSafari) {
40107             var tbl = tcfg.container.child('table', true);
40108             tbl.setAttribute('width', '100%');
40109         }
40110         */
40111         
40112     }
40113    
40114
40115
40116     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40117 };
40118
40119 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40120     /*
40121      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40122      */
40123     tabPosition : "top",
40124     /*
40125      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40126      */
40127     currentTabWidth : 0,
40128     /*
40129      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40130      */
40131     minTabWidth : 40,
40132     /*
40133      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40134      */
40135     maxTabWidth : 250,
40136     /*
40137      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40138      */
40139     preferredTabWidth : 175,
40140     /*
40141      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40142      */
40143     resizeTabs : false,
40144     /*
40145      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40146      */
40147     monitorResize : true,
40148     /*
40149      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40150      */
40151     toolbar : false,  // set by caller..
40152     
40153     region : false, /// set by caller
40154     
40155     disableTooltips : true, // not used yet...
40156
40157     /**
40158      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40159      * @param {String} id The id of the div to use <b>or create</b>
40160      * @param {String} text The text for the tab
40161      * @param {String} content (optional) Content to put in the TabPanelItem body
40162      * @param {Boolean} closable (optional) True to create a close icon on the tab
40163      * @return {Roo.TabPanelItem} The created TabPanelItem
40164      */
40165     addTab : function(id, text, content, closable, tpl)
40166     {
40167         var item = new Roo.bootstrap.panel.TabItem({
40168             panel: this,
40169             id : id,
40170             text : text,
40171             closable : closable,
40172             tpl : tpl
40173         });
40174         this.addTabItem(item);
40175         if(content){
40176             item.setContent(content);
40177         }
40178         return item;
40179     },
40180
40181     /**
40182      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40183      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40184      * @return {Roo.TabPanelItem}
40185      */
40186     getTab : function(id){
40187         return this.items[id];
40188     },
40189
40190     /**
40191      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40192      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40193      */
40194     hideTab : function(id){
40195         var t = this.items[id];
40196         if(!t.isHidden()){
40197            t.setHidden(true);
40198            this.hiddenCount++;
40199            this.autoSizeTabs();
40200         }
40201     },
40202
40203     /**
40204      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40205      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40206      */
40207     unhideTab : function(id){
40208         var t = this.items[id];
40209         if(t.isHidden()){
40210            t.setHidden(false);
40211            this.hiddenCount--;
40212            this.autoSizeTabs();
40213         }
40214     },
40215
40216     /**
40217      * Adds an existing {@link Roo.TabPanelItem}.
40218      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40219      */
40220     addTabItem : function(item)
40221     {
40222         this.items[item.id] = item;
40223         this.items.push(item);
40224         this.autoSizeTabs();
40225       //  if(this.resizeTabs){
40226     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40227   //         this.autoSizeTabs();
40228 //        }else{
40229 //            item.autoSize();
40230        // }
40231     },
40232
40233     /**
40234      * Removes a {@link Roo.TabPanelItem}.
40235      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40236      */
40237     removeTab : function(id){
40238         var items = this.items;
40239         var tab = items[id];
40240         if(!tab) { return; }
40241         var index = items.indexOf(tab);
40242         if(this.active == tab && items.length > 1){
40243             var newTab = this.getNextAvailable(index);
40244             if(newTab) {
40245                 newTab.activate();
40246             }
40247         }
40248         this.stripEl.dom.removeChild(tab.pnode.dom);
40249         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40250             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40251         }
40252         items.splice(index, 1);
40253         delete this.items[tab.id];
40254         tab.fireEvent("close", tab);
40255         tab.purgeListeners();
40256         this.autoSizeTabs();
40257     },
40258
40259     getNextAvailable : function(start){
40260         var items = this.items;
40261         var index = start;
40262         // look for a next tab that will slide over to
40263         // replace the one being removed
40264         while(index < items.length){
40265             var item = items[++index];
40266             if(item && !item.isHidden()){
40267                 return item;
40268             }
40269         }
40270         // if one isn't found select the previous tab (on the left)
40271         index = start;
40272         while(index >= 0){
40273             var item = items[--index];
40274             if(item && !item.isHidden()){
40275                 return item;
40276             }
40277         }
40278         return null;
40279     },
40280
40281     /**
40282      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40283      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40284      */
40285     disableTab : function(id){
40286         var tab = this.items[id];
40287         if(tab && this.active != tab){
40288             tab.disable();
40289         }
40290     },
40291
40292     /**
40293      * Enables a {@link Roo.TabPanelItem} that is disabled.
40294      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40295      */
40296     enableTab : function(id){
40297         var tab = this.items[id];
40298         tab.enable();
40299     },
40300
40301     /**
40302      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40303      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40304      * @return {Roo.TabPanelItem} The TabPanelItem.
40305      */
40306     activate : function(id)
40307     {
40308         //Roo.log('activite:'  + id);
40309         
40310         var tab = this.items[id];
40311         if(!tab){
40312             return null;
40313         }
40314         if(tab == this.active || tab.disabled){
40315             return tab;
40316         }
40317         var e = {};
40318         this.fireEvent("beforetabchange", this, e, tab);
40319         if(e.cancel !== true && !tab.disabled){
40320             if(this.active){
40321                 this.active.hide();
40322             }
40323             this.active = this.items[id];
40324             this.active.show();
40325             this.fireEvent("tabchange", this, this.active);
40326         }
40327         return tab;
40328     },
40329
40330     /**
40331      * Gets the active {@link Roo.TabPanelItem}.
40332      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40333      */
40334     getActiveTab : function(){
40335         return this.active;
40336     },
40337
40338     /**
40339      * Updates the tab body element to fit the height of the container element
40340      * for overflow scrolling
40341      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40342      */
40343     syncHeight : function(targetHeight){
40344         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40345         var bm = this.bodyEl.getMargins();
40346         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40347         this.bodyEl.setHeight(newHeight);
40348         return newHeight;
40349     },
40350
40351     onResize : function(){
40352         if(this.monitorResize){
40353             this.autoSizeTabs();
40354         }
40355     },
40356
40357     /**
40358      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40359      */
40360     beginUpdate : function(){
40361         this.updating = true;
40362     },
40363
40364     /**
40365      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40366      */
40367     endUpdate : function(){
40368         this.updating = false;
40369         this.autoSizeTabs();
40370     },
40371
40372     /**
40373      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40374      */
40375     autoSizeTabs : function()
40376     {
40377         var count = this.items.length;
40378         var vcount = count - this.hiddenCount;
40379         
40380         if (vcount < 2) {
40381             this.stripEl.hide();
40382         } else {
40383             this.stripEl.show();
40384         }
40385         
40386         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40387             return;
40388         }
40389         
40390         
40391         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40392         var availWidth = Math.floor(w / vcount);
40393         var b = this.stripBody;
40394         if(b.getWidth() > w){
40395             var tabs = this.items;
40396             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40397             if(availWidth < this.minTabWidth){
40398                 /*if(!this.sleft){    // incomplete scrolling code
40399                     this.createScrollButtons();
40400                 }
40401                 this.showScroll();
40402                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40403             }
40404         }else{
40405             if(this.currentTabWidth < this.preferredTabWidth){
40406                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40407             }
40408         }
40409     },
40410
40411     /**
40412      * Returns the number of tabs in this TabPanel.
40413      * @return {Number}
40414      */
40415      getCount : function(){
40416          return this.items.length;
40417      },
40418
40419     /**
40420      * Resizes all the tabs to the passed width
40421      * @param {Number} The new width
40422      */
40423     setTabWidth : function(width){
40424         this.currentTabWidth = width;
40425         for(var i = 0, len = this.items.length; i < len; i++) {
40426                 if(!this.items[i].isHidden()) {
40427                 this.items[i].setWidth(width);
40428             }
40429         }
40430     },
40431
40432     /**
40433      * Destroys this TabPanel
40434      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40435      */
40436     destroy : function(removeEl){
40437         Roo.EventManager.removeResizeListener(this.onResize, this);
40438         for(var i = 0, len = this.items.length; i < len; i++){
40439             this.items[i].purgeListeners();
40440         }
40441         if(removeEl === true){
40442             this.el.update("");
40443             this.el.remove();
40444         }
40445     },
40446     
40447     createStrip : function(container)
40448     {
40449         var strip = document.createElement("nav");
40450         strip.className = Roo.bootstrap.version == 4 ?
40451             "navbar-light bg-light" : 
40452             "navbar navbar-default"; //"x-tabs-wrap";
40453         container.appendChild(strip);
40454         return strip;
40455     },
40456     
40457     createStripList : function(strip)
40458     {
40459         // div wrapper for retard IE
40460         // returns the "tr" element.
40461         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40462         //'<div class="x-tabs-strip-wrap">'+
40463           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40464           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40465         return strip.firstChild; //.firstChild.firstChild.firstChild;
40466     },
40467     createBody : function(container)
40468     {
40469         var body = document.createElement("div");
40470         Roo.id(body, "tab-body");
40471         //Roo.fly(body).addClass("x-tabs-body");
40472         Roo.fly(body).addClass("tab-content");
40473         container.appendChild(body);
40474         return body;
40475     },
40476     createItemBody :function(bodyEl, id){
40477         var body = Roo.getDom(id);
40478         if(!body){
40479             body = document.createElement("div");
40480             body.id = id;
40481         }
40482         //Roo.fly(body).addClass("x-tabs-item-body");
40483         Roo.fly(body).addClass("tab-pane");
40484          bodyEl.insertBefore(body, bodyEl.firstChild);
40485         return body;
40486     },
40487     /** @private */
40488     createStripElements :  function(stripEl, text, closable, tpl)
40489     {
40490         var td = document.createElement("li"); // was td..
40491         td.className = 'nav-item';
40492         
40493         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40494         
40495         
40496         stripEl.appendChild(td);
40497         /*if(closable){
40498             td.className = "x-tabs-closable";
40499             if(!this.closeTpl){
40500                 this.closeTpl = new Roo.Template(
40501                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40502                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40503                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40504                 );
40505             }
40506             var el = this.closeTpl.overwrite(td, {"text": text});
40507             var close = el.getElementsByTagName("div")[0];
40508             var inner = el.getElementsByTagName("em")[0];
40509             return {"el": el, "close": close, "inner": inner};
40510         } else {
40511         */
40512         // not sure what this is..
40513 //            if(!this.tabTpl){
40514                 //this.tabTpl = new Roo.Template(
40515                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40516                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40517                 //);
40518 //                this.tabTpl = new Roo.Template(
40519 //                   '<a href="#">' +
40520 //                   '<span unselectable="on"' +
40521 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40522 //                            ' >{text}</span></a>'
40523 //                );
40524 //                
40525 //            }
40526
40527
40528             var template = tpl || this.tabTpl || false;
40529             
40530             if(!template){
40531                 template =  new Roo.Template(
40532                         Roo.bootstrap.version == 4 ? 
40533                             (
40534                                 '<a class="nav-link" href="#" unselectable="on"' +
40535                                      (this.disableTooltips ? '' : ' title="{text}"') +
40536                                      ' >{text}</a>'
40537                             ) : (
40538                                 '<a class="nav-link" href="#">' +
40539                                 '<span unselectable="on"' +
40540                                          (this.disableTooltips ? '' : ' title="{text}"') +
40541                                     ' >{text}</span></a>'
40542                             )
40543                 );
40544             }
40545             
40546             switch (typeof(template)) {
40547                 case 'object' :
40548                     break;
40549                 case 'string' :
40550                     template = new Roo.Template(template);
40551                     break;
40552                 default :
40553                     break;
40554             }
40555             
40556             var el = template.overwrite(td, {"text": text});
40557             
40558             var inner = el.getElementsByTagName("span")[0];
40559             
40560             return {"el": el, "inner": inner};
40561             
40562     }
40563         
40564     
40565 });
40566
40567 /**
40568  * @class Roo.TabPanelItem
40569  * @extends Roo.util.Observable
40570  * Represents an individual item (tab plus body) in a TabPanel.
40571  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40572  * @param {String} id The id of this TabPanelItem
40573  * @param {String} text The text for the tab of this TabPanelItem
40574  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40575  */
40576 Roo.bootstrap.panel.TabItem = function(config){
40577     /**
40578      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40579      * @type Roo.TabPanel
40580      */
40581     this.tabPanel = config.panel;
40582     /**
40583      * The id for this TabPanelItem
40584      * @type String
40585      */
40586     this.id = config.id;
40587     /** @private */
40588     this.disabled = false;
40589     /** @private */
40590     this.text = config.text;
40591     /** @private */
40592     this.loaded = false;
40593     this.closable = config.closable;
40594
40595     /**
40596      * The body element for this TabPanelItem.
40597      * @type Roo.Element
40598      */
40599     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40600     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40601     this.bodyEl.setStyle("display", "block");
40602     this.bodyEl.setStyle("zoom", "1");
40603     //this.hideAction();
40604
40605     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40606     /** @private */
40607     this.el = Roo.get(els.el);
40608     this.inner = Roo.get(els.inner, true);
40609      this.textEl = Roo.bootstrap.version == 4 ?
40610         this.el : Roo.get(this.el.dom.firstChild, true);
40611
40612     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40613     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40614
40615     
40616 //    this.el.on("mousedown", this.onTabMouseDown, this);
40617     this.el.on("click", this.onTabClick, this);
40618     /** @private */
40619     if(config.closable){
40620         var c = Roo.get(els.close, true);
40621         c.dom.title = this.closeText;
40622         c.addClassOnOver("close-over");
40623         c.on("click", this.closeClick, this);
40624      }
40625
40626     this.addEvents({
40627          /**
40628          * @event activate
40629          * Fires when this tab becomes the active tab.
40630          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40631          * @param {Roo.TabPanelItem} this
40632          */
40633         "activate": true,
40634         /**
40635          * @event beforeclose
40636          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40637          * @param {Roo.TabPanelItem} this
40638          * @param {Object} e Set cancel to true on this object to cancel the close.
40639          */
40640         "beforeclose": true,
40641         /**
40642          * @event close
40643          * Fires when this tab is closed.
40644          * @param {Roo.TabPanelItem} this
40645          */
40646          "close": true,
40647         /**
40648          * @event deactivate
40649          * Fires when this tab is no longer the active tab.
40650          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40651          * @param {Roo.TabPanelItem} this
40652          */
40653          "deactivate" : true
40654     });
40655     this.hidden = false;
40656
40657     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40658 };
40659
40660 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40661            {
40662     purgeListeners : function(){
40663        Roo.util.Observable.prototype.purgeListeners.call(this);
40664        this.el.removeAllListeners();
40665     },
40666     /**
40667      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40668      */
40669     show : function(){
40670         this.status_node.addClass("active");
40671         this.showAction();
40672         if(Roo.isOpera){
40673             this.tabPanel.stripWrap.repaint();
40674         }
40675         this.fireEvent("activate", this.tabPanel, this);
40676     },
40677
40678     /**
40679      * Returns true if this tab is the active tab.
40680      * @return {Boolean}
40681      */
40682     isActive : function(){
40683         return this.tabPanel.getActiveTab() == this;
40684     },
40685
40686     /**
40687      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40688      */
40689     hide : function(){
40690         this.status_node.removeClass("active");
40691         this.hideAction();
40692         this.fireEvent("deactivate", this.tabPanel, this);
40693     },
40694
40695     hideAction : function(){
40696         this.bodyEl.hide();
40697         this.bodyEl.setStyle("position", "absolute");
40698         this.bodyEl.setLeft("-20000px");
40699         this.bodyEl.setTop("-20000px");
40700     },
40701
40702     showAction : function(){
40703         this.bodyEl.setStyle("position", "relative");
40704         this.bodyEl.setTop("");
40705         this.bodyEl.setLeft("");
40706         this.bodyEl.show();
40707     },
40708
40709     /**
40710      * Set the tooltip for the tab.
40711      * @param {String} tooltip The tab's tooltip
40712      */
40713     setTooltip : function(text){
40714         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40715             this.textEl.dom.qtip = text;
40716             this.textEl.dom.removeAttribute('title');
40717         }else{
40718             this.textEl.dom.title = text;
40719         }
40720     },
40721
40722     onTabClick : function(e){
40723         e.preventDefault();
40724         this.tabPanel.activate(this.id);
40725     },
40726
40727     onTabMouseDown : function(e){
40728         e.preventDefault();
40729         this.tabPanel.activate(this.id);
40730     },
40731 /*
40732     getWidth : function(){
40733         return this.inner.getWidth();
40734     },
40735
40736     setWidth : function(width){
40737         var iwidth = width - this.linode.getPadding("lr");
40738         this.inner.setWidth(iwidth);
40739         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40740         this.linode.setWidth(width);
40741     },
40742 */
40743     /**
40744      * Show or hide the tab
40745      * @param {Boolean} hidden True to hide or false to show.
40746      */
40747     setHidden : function(hidden){
40748         this.hidden = hidden;
40749         this.linode.setStyle("display", hidden ? "none" : "");
40750     },
40751
40752     /**
40753      * Returns true if this tab is "hidden"
40754      * @return {Boolean}
40755      */
40756     isHidden : function(){
40757         return this.hidden;
40758     },
40759
40760     /**
40761      * Returns the text for this tab
40762      * @return {String}
40763      */
40764     getText : function(){
40765         return this.text;
40766     },
40767     /*
40768     autoSize : function(){
40769         //this.el.beginMeasure();
40770         this.textEl.setWidth(1);
40771         /*
40772          *  #2804 [new] Tabs in Roojs
40773          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40774          */
40775         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40776         //this.el.endMeasure();
40777     //},
40778
40779     /**
40780      * Sets the text for the tab (Note: this also sets the tooltip text)
40781      * @param {String} text The tab's text and tooltip
40782      */
40783     setText : function(text){
40784         this.text = text;
40785         this.textEl.update(text);
40786         this.setTooltip(text);
40787         //if(!this.tabPanel.resizeTabs){
40788         //    this.autoSize();
40789         //}
40790     },
40791     /**
40792      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40793      */
40794     activate : function(){
40795         this.tabPanel.activate(this.id);
40796     },
40797
40798     /**
40799      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40800      */
40801     disable : function(){
40802         if(this.tabPanel.active != this){
40803             this.disabled = true;
40804             this.status_node.addClass("disabled");
40805         }
40806     },
40807
40808     /**
40809      * Enables this TabPanelItem if it was previously disabled.
40810      */
40811     enable : function(){
40812         this.disabled = false;
40813         this.status_node.removeClass("disabled");
40814     },
40815
40816     /**
40817      * Sets the content for this TabPanelItem.
40818      * @param {String} content The content
40819      * @param {Boolean} loadScripts true to look for and load scripts
40820      */
40821     setContent : function(content, loadScripts){
40822         this.bodyEl.update(content, loadScripts);
40823     },
40824
40825     /**
40826      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40827      * @return {Roo.UpdateManager} The UpdateManager
40828      */
40829     getUpdateManager : function(){
40830         return this.bodyEl.getUpdateManager();
40831     },
40832
40833     /**
40834      * Set a URL to be used to load the content for this TabPanelItem.
40835      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40836      * @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)
40837      * @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)
40838      * @return {Roo.UpdateManager} The UpdateManager
40839      */
40840     setUrl : function(url, params, loadOnce){
40841         if(this.refreshDelegate){
40842             this.un('activate', this.refreshDelegate);
40843         }
40844         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40845         this.on("activate", this.refreshDelegate);
40846         return this.bodyEl.getUpdateManager();
40847     },
40848
40849     /** @private */
40850     _handleRefresh : function(url, params, loadOnce){
40851         if(!loadOnce || !this.loaded){
40852             var updater = this.bodyEl.getUpdateManager();
40853             updater.update(url, params, this._setLoaded.createDelegate(this));
40854         }
40855     },
40856
40857     /**
40858      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40859      *   Will fail silently if the setUrl method has not been called.
40860      *   This does not activate the panel, just updates its content.
40861      */
40862     refresh : function(){
40863         if(this.refreshDelegate){
40864            this.loaded = false;
40865            this.refreshDelegate();
40866         }
40867     },
40868
40869     /** @private */
40870     _setLoaded : function(){
40871         this.loaded = true;
40872     },
40873
40874     /** @private */
40875     closeClick : function(e){
40876         var o = {};
40877         e.stopEvent();
40878         this.fireEvent("beforeclose", this, o);
40879         if(o.cancel !== true){
40880             this.tabPanel.removeTab(this.id);
40881         }
40882     },
40883     /**
40884      * The text displayed in the tooltip for the close icon.
40885      * @type String
40886      */
40887     closeText : "Close this tab"
40888 });
40889 /**
40890 *    This script refer to:
40891 *    Title: International Telephone Input
40892 *    Author: Jack O'Connor
40893 *    Code version:  v12.1.12
40894 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40895 **/
40896
40897 Roo.bootstrap.PhoneInputData = function() {
40898     var d = [
40899       [
40900         "Afghanistan (‫افغانستان‬‎)",
40901         "af",
40902         "93"
40903       ],
40904       [
40905         "Albania (Shqipëri)",
40906         "al",
40907         "355"
40908       ],
40909       [
40910         "Algeria (‫الجزائر‬‎)",
40911         "dz",
40912         "213"
40913       ],
40914       [
40915         "American Samoa",
40916         "as",
40917         "1684"
40918       ],
40919       [
40920         "Andorra",
40921         "ad",
40922         "376"
40923       ],
40924       [
40925         "Angola",
40926         "ao",
40927         "244"
40928       ],
40929       [
40930         "Anguilla",
40931         "ai",
40932         "1264"
40933       ],
40934       [
40935         "Antigua and Barbuda",
40936         "ag",
40937         "1268"
40938       ],
40939       [
40940         "Argentina",
40941         "ar",
40942         "54"
40943       ],
40944       [
40945         "Armenia (Հայաստան)",
40946         "am",
40947         "374"
40948       ],
40949       [
40950         "Aruba",
40951         "aw",
40952         "297"
40953       ],
40954       [
40955         "Australia",
40956         "au",
40957         "61",
40958         0
40959       ],
40960       [
40961         "Austria (Österreich)",
40962         "at",
40963         "43"
40964       ],
40965       [
40966         "Azerbaijan (Azərbaycan)",
40967         "az",
40968         "994"
40969       ],
40970       [
40971         "Bahamas",
40972         "bs",
40973         "1242"
40974       ],
40975       [
40976         "Bahrain (‫البحرين‬‎)",
40977         "bh",
40978         "973"
40979       ],
40980       [
40981         "Bangladesh (বাংলাদেশ)",
40982         "bd",
40983         "880"
40984       ],
40985       [
40986         "Barbados",
40987         "bb",
40988         "1246"
40989       ],
40990       [
40991         "Belarus (Беларусь)",
40992         "by",
40993         "375"
40994       ],
40995       [
40996         "Belgium (België)",
40997         "be",
40998         "32"
40999       ],
41000       [
41001         "Belize",
41002         "bz",
41003         "501"
41004       ],
41005       [
41006         "Benin (Bénin)",
41007         "bj",
41008         "229"
41009       ],
41010       [
41011         "Bermuda",
41012         "bm",
41013         "1441"
41014       ],
41015       [
41016         "Bhutan (འབྲུག)",
41017         "bt",
41018         "975"
41019       ],
41020       [
41021         "Bolivia",
41022         "bo",
41023         "591"
41024       ],
41025       [
41026         "Bosnia and Herzegovina (Босна и Херцеговина)",
41027         "ba",
41028         "387"
41029       ],
41030       [
41031         "Botswana",
41032         "bw",
41033         "267"
41034       ],
41035       [
41036         "Brazil (Brasil)",
41037         "br",
41038         "55"
41039       ],
41040       [
41041         "British Indian Ocean Territory",
41042         "io",
41043         "246"
41044       ],
41045       [
41046         "British Virgin Islands",
41047         "vg",
41048         "1284"
41049       ],
41050       [
41051         "Brunei",
41052         "bn",
41053         "673"
41054       ],
41055       [
41056         "Bulgaria (България)",
41057         "bg",
41058         "359"
41059       ],
41060       [
41061         "Burkina Faso",
41062         "bf",
41063         "226"
41064       ],
41065       [
41066         "Burundi (Uburundi)",
41067         "bi",
41068         "257"
41069       ],
41070       [
41071         "Cambodia (កម្ពុជា)",
41072         "kh",
41073         "855"
41074       ],
41075       [
41076         "Cameroon (Cameroun)",
41077         "cm",
41078         "237"
41079       ],
41080       [
41081         "Canada",
41082         "ca",
41083         "1",
41084         1,
41085         ["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"]
41086       ],
41087       [
41088         "Cape Verde (Kabu Verdi)",
41089         "cv",
41090         "238"
41091       ],
41092       [
41093         "Caribbean Netherlands",
41094         "bq",
41095         "599",
41096         1
41097       ],
41098       [
41099         "Cayman Islands",
41100         "ky",
41101         "1345"
41102       ],
41103       [
41104         "Central African Republic (République centrafricaine)",
41105         "cf",
41106         "236"
41107       ],
41108       [
41109         "Chad (Tchad)",
41110         "td",
41111         "235"
41112       ],
41113       [
41114         "Chile",
41115         "cl",
41116         "56"
41117       ],
41118       [
41119         "China (中国)",
41120         "cn",
41121         "86"
41122       ],
41123       [
41124         "Christmas Island",
41125         "cx",
41126         "61",
41127         2
41128       ],
41129       [
41130         "Cocos (Keeling) Islands",
41131         "cc",
41132         "61",
41133         1
41134       ],
41135       [
41136         "Colombia",
41137         "co",
41138         "57"
41139       ],
41140       [
41141         "Comoros (‫جزر القمر‬‎)",
41142         "km",
41143         "269"
41144       ],
41145       [
41146         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41147         "cd",
41148         "243"
41149       ],
41150       [
41151         "Congo (Republic) (Congo-Brazzaville)",
41152         "cg",
41153         "242"
41154       ],
41155       [
41156         "Cook Islands",
41157         "ck",
41158         "682"
41159       ],
41160       [
41161         "Costa Rica",
41162         "cr",
41163         "506"
41164       ],
41165       [
41166         "Côte d’Ivoire",
41167         "ci",
41168         "225"
41169       ],
41170       [
41171         "Croatia (Hrvatska)",
41172         "hr",
41173         "385"
41174       ],
41175       [
41176         "Cuba",
41177         "cu",
41178         "53"
41179       ],
41180       [
41181         "Curaçao",
41182         "cw",
41183         "599",
41184         0
41185       ],
41186       [
41187         "Cyprus (Κύπρος)",
41188         "cy",
41189         "357"
41190       ],
41191       [
41192         "Czech Republic (Česká republika)",
41193         "cz",
41194         "420"
41195       ],
41196       [
41197         "Denmark (Danmark)",
41198         "dk",
41199         "45"
41200       ],
41201       [
41202         "Djibouti",
41203         "dj",
41204         "253"
41205       ],
41206       [
41207         "Dominica",
41208         "dm",
41209         "1767"
41210       ],
41211       [
41212         "Dominican Republic (República Dominicana)",
41213         "do",
41214         "1",
41215         2,
41216         ["809", "829", "849"]
41217       ],
41218       [
41219         "Ecuador",
41220         "ec",
41221         "593"
41222       ],
41223       [
41224         "Egypt (‫مصر‬‎)",
41225         "eg",
41226         "20"
41227       ],
41228       [
41229         "El Salvador",
41230         "sv",
41231         "503"
41232       ],
41233       [
41234         "Equatorial Guinea (Guinea Ecuatorial)",
41235         "gq",
41236         "240"
41237       ],
41238       [
41239         "Eritrea",
41240         "er",
41241         "291"
41242       ],
41243       [
41244         "Estonia (Eesti)",
41245         "ee",
41246         "372"
41247       ],
41248       [
41249         "Ethiopia",
41250         "et",
41251         "251"
41252       ],
41253       [
41254         "Falkland Islands (Islas Malvinas)",
41255         "fk",
41256         "500"
41257       ],
41258       [
41259         "Faroe Islands (Føroyar)",
41260         "fo",
41261         "298"
41262       ],
41263       [
41264         "Fiji",
41265         "fj",
41266         "679"
41267       ],
41268       [
41269         "Finland (Suomi)",
41270         "fi",
41271         "358",
41272         0
41273       ],
41274       [
41275         "France",
41276         "fr",
41277         "33"
41278       ],
41279       [
41280         "French Guiana (Guyane française)",
41281         "gf",
41282         "594"
41283       ],
41284       [
41285         "French Polynesia (Polynésie française)",
41286         "pf",
41287         "689"
41288       ],
41289       [
41290         "Gabon",
41291         "ga",
41292         "241"
41293       ],
41294       [
41295         "Gambia",
41296         "gm",
41297         "220"
41298       ],
41299       [
41300         "Georgia (საქართველო)",
41301         "ge",
41302         "995"
41303       ],
41304       [
41305         "Germany (Deutschland)",
41306         "de",
41307         "49"
41308       ],
41309       [
41310         "Ghana (Gaana)",
41311         "gh",
41312         "233"
41313       ],
41314       [
41315         "Gibraltar",
41316         "gi",
41317         "350"
41318       ],
41319       [
41320         "Greece (Ελλάδα)",
41321         "gr",
41322         "30"
41323       ],
41324       [
41325         "Greenland (Kalaallit Nunaat)",
41326         "gl",
41327         "299"
41328       ],
41329       [
41330         "Grenada",
41331         "gd",
41332         "1473"
41333       ],
41334       [
41335         "Guadeloupe",
41336         "gp",
41337         "590",
41338         0
41339       ],
41340       [
41341         "Guam",
41342         "gu",
41343         "1671"
41344       ],
41345       [
41346         "Guatemala",
41347         "gt",
41348         "502"
41349       ],
41350       [
41351         "Guernsey",
41352         "gg",
41353         "44",
41354         1
41355       ],
41356       [
41357         "Guinea (Guinée)",
41358         "gn",
41359         "224"
41360       ],
41361       [
41362         "Guinea-Bissau (Guiné Bissau)",
41363         "gw",
41364         "245"
41365       ],
41366       [
41367         "Guyana",
41368         "gy",
41369         "592"
41370       ],
41371       [
41372         "Haiti",
41373         "ht",
41374         "509"
41375       ],
41376       [
41377         "Honduras",
41378         "hn",
41379         "504"
41380       ],
41381       [
41382         "Hong Kong (香港)",
41383         "hk",
41384         "852"
41385       ],
41386       [
41387         "Hungary (Magyarország)",
41388         "hu",
41389         "36"
41390       ],
41391       [
41392         "Iceland (Ísland)",
41393         "is",
41394         "354"
41395       ],
41396       [
41397         "India (भारत)",
41398         "in",
41399         "91"
41400       ],
41401       [
41402         "Indonesia",
41403         "id",
41404         "62"
41405       ],
41406       [
41407         "Iran (‫ایران‬‎)",
41408         "ir",
41409         "98"
41410       ],
41411       [
41412         "Iraq (‫العراق‬‎)",
41413         "iq",
41414         "964"
41415       ],
41416       [
41417         "Ireland",
41418         "ie",
41419         "353"
41420       ],
41421       [
41422         "Isle of Man",
41423         "im",
41424         "44",
41425         2
41426       ],
41427       [
41428         "Israel (‫ישראל‬‎)",
41429         "il",
41430         "972"
41431       ],
41432       [
41433         "Italy (Italia)",
41434         "it",
41435         "39",
41436         0
41437       ],
41438       [
41439         "Jamaica",
41440         "jm",
41441         "1876"
41442       ],
41443       [
41444         "Japan (日本)",
41445         "jp",
41446         "81"
41447       ],
41448       [
41449         "Jersey",
41450         "je",
41451         "44",
41452         3
41453       ],
41454       [
41455         "Jordan (‫الأردن‬‎)",
41456         "jo",
41457         "962"
41458       ],
41459       [
41460         "Kazakhstan (Казахстан)",
41461         "kz",
41462         "7",
41463         1
41464       ],
41465       [
41466         "Kenya",
41467         "ke",
41468         "254"
41469       ],
41470       [
41471         "Kiribati",
41472         "ki",
41473         "686"
41474       ],
41475       [
41476         "Kosovo",
41477         "xk",
41478         "383"
41479       ],
41480       [
41481         "Kuwait (‫الكويت‬‎)",
41482         "kw",
41483         "965"
41484       ],
41485       [
41486         "Kyrgyzstan (Кыргызстан)",
41487         "kg",
41488         "996"
41489       ],
41490       [
41491         "Laos (ລາວ)",
41492         "la",
41493         "856"
41494       ],
41495       [
41496         "Latvia (Latvija)",
41497         "lv",
41498         "371"
41499       ],
41500       [
41501         "Lebanon (‫لبنان‬‎)",
41502         "lb",
41503         "961"
41504       ],
41505       [
41506         "Lesotho",
41507         "ls",
41508         "266"
41509       ],
41510       [
41511         "Liberia",
41512         "lr",
41513         "231"
41514       ],
41515       [
41516         "Libya (‫ليبيا‬‎)",
41517         "ly",
41518         "218"
41519       ],
41520       [
41521         "Liechtenstein",
41522         "li",
41523         "423"
41524       ],
41525       [
41526         "Lithuania (Lietuva)",
41527         "lt",
41528         "370"
41529       ],
41530       [
41531         "Luxembourg",
41532         "lu",
41533         "352"
41534       ],
41535       [
41536         "Macau (澳門)",
41537         "mo",
41538         "853"
41539       ],
41540       [
41541         "Macedonia (FYROM) (Македонија)",
41542         "mk",
41543         "389"
41544       ],
41545       [
41546         "Madagascar (Madagasikara)",
41547         "mg",
41548         "261"
41549       ],
41550       [
41551         "Malawi",
41552         "mw",
41553         "265"
41554       ],
41555       [
41556         "Malaysia",
41557         "my",
41558         "60"
41559       ],
41560       [
41561         "Maldives",
41562         "mv",
41563         "960"
41564       ],
41565       [
41566         "Mali",
41567         "ml",
41568         "223"
41569       ],
41570       [
41571         "Malta",
41572         "mt",
41573         "356"
41574       ],
41575       [
41576         "Marshall Islands",
41577         "mh",
41578         "692"
41579       ],
41580       [
41581         "Martinique",
41582         "mq",
41583         "596"
41584       ],
41585       [
41586         "Mauritania (‫موريتانيا‬‎)",
41587         "mr",
41588         "222"
41589       ],
41590       [
41591         "Mauritius (Moris)",
41592         "mu",
41593         "230"
41594       ],
41595       [
41596         "Mayotte",
41597         "yt",
41598         "262",
41599         1
41600       ],
41601       [
41602         "Mexico (México)",
41603         "mx",
41604         "52"
41605       ],
41606       [
41607         "Micronesia",
41608         "fm",
41609         "691"
41610       ],
41611       [
41612         "Moldova (Republica Moldova)",
41613         "md",
41614         "373"
41615       ],
41616       [
41617         "Monaco",
41618         "mc",
41619         "377"
41620       ],
41621       [
41622         "Mongolia (Монгол)",
41623         "mn",
41624         "976"
41625       ],
41626       [
41627         "Montenegro (Crna Gora)",
41628         "me",
41629         "382"
41630       ],
41631       [
41632         "Montserrat",
41633         "ms",
41634         "1664"
41635       ],
41636       [
41637         "Morocco (‫المغرب‬‎)",
41638         "ma",
41639         "212",
41640         0
41641       ],
41642       [
41643         "Mozambique (Moçambique)",
41644         "mz",
41645         "258"
41646       ],
41647       [
41648         "Myanmar (Burma) (မြန်မာ)",
41649         "mm",
41650         "95"
41651       ],
41652       [
41653         "Namibia (Namibië)",
41654         "na",
41655         "264"
41656       ],
41657       [
41658         "Nauru",
41659         "nr",
41660         "674"
41661       ],
41662       [
41663         "Nepal (नेपाल)",
41664         "np",
41665         "977"
41666       ],
41667       [
41668         "Netherlands (Nederland)",
41669         "nl",
41670         "31"
41671       ],
41672       [
41673         "New Caledonia (Nouvelle-Calédonie)",
41674         "nc",
41675         "687"
41676       ],
41677       [
41678         "New Zealand",
41679         "nz",
41680         "64"
41681       ],
41682       [
41683         "Nicaragua",
41684         "ni",
41685         "505"
41686       ],
41687       [
41688         "Niger (Nijar)",
41689         "ne",
41690         "227"
41691       ],
41692       [
41693         "Nigeria",
41694         "ng",
41695         "234"
41696       ],
41697       [
41698         "Niue",
41699         "nu",
41700         "683"
41701       ],
41702       [
41703         "Norfolk Island",
41704         "nf",
41705         "672"
41706       ],
41707       [
41708         "North Korea (조선 민주주의 인민 공화국)",
41709         "kp",
41710         "850"
41711       ],
41712       [
41713         "Northern Mariana Islands",
41714         "mp",
41715         "1670"
41716       ],
41717       [
41718         "Norway (Norge)",
41719         "no",
41720         "47",
41721         0
41722       ],
41723       [
41724         "Oman (‫عُمان‬‎)",
41725         "om",
41726         "968"
41727       ],
41728       [
41729         "Pakistan (‫پاکستان‬‎)",
41730         "pk",
41731         "92"
41732       ],
41733       [
41734         "Palau",
41735         "pw",
41736         "680"
41737       ],
41738       [
41739         "Palestine (‫فلسطين‬‎)",
41740         "ps",
41741         "970"
41742       ],
41743       [
41744         "Panama (Panamá)",
41745         "pa",
41746         "507"
41747       ],
41748       [
41749         "Papua New Guinea",
41750         "pg",
41751         "675"
41752       ],
41753       [
41754         "Paraguay",
41755         "py",
41756         "595"
41757       ],
41758       [
41759         "Peru (Perú)",
41760         "pe",
41761         "51"
41762       ],
41763       [
41764         "Philippines",
41765         "ph",
41766         "63"
41767       ],
41768       [
41769         "Poland (Polska)",
41770         "pl",
41771         "48"
41772       ],
41773       [
41774         "Portugal",
41775         "pt",
41776         "351"
41777       ],
41778       [
41779         "Puerto Rico",
41780         "pr",
41781         "1",
41782         3,
41783         ["787", "939"]
41784       ],
41785       [
41786         "Qatar (‫قطر‬‎)",
41787         "qa",
41788         "974"
41789       ],
41790       [
41791         "Réunion (La Réunion)",
41792         "re",
41793         "262",
41794         0
41795       ],
41796       [
41797         "Romania (România)",
41798         "ro",
41799         "40"
41800       ],
41801       [
41802         "Russia (Россия)",
41803         "ru",
41804         "7",
41805         0
41806       ],
41807       [
41808         "Rwanda",
41809         "rw",
41810         "250"
41811       ],
41812       [
41813         "Saint Barthélemy",
41814         "bl",
41815         "590",
41816         1
41817       ],
41818       [
41819         "Saint Helena",
41820         "sh",
41821         "290"
41822       ],
41823       [
41824         "Saint Kitts and Nevis",
41825         "kn",
41826         "1869"
41827       ],
41828       [
41829         "Saint Lucia",
41830         "lc",
41831         "1758"
41832       ],
41833       [
41834         "Saint Martin (Saint-Martin (partie française))",
41835         "mf",
41836         "590",
41837         2
41838       ],
41839       [
41840         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41841         "pm",
41842         "508"
41843       ],
41844       [
41845         "Saint Vincent and the Grenadines",
41846         "vc",
41847         "1784"
41848       ],
41849       [
41850         "Samoa",
41851         "ws",
41852         "685"
41853       ],
41854       [
41855         "San Marino",
41856         "sm",
41857         "378"
41858       ],
41859       [
41860         "São Tomé and Príncipe (São Tomé e Príncipe)",
41861         "st",
41862         "239"
41863       ],
41864       [
41865         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41866         "sa",
41867         "966"
41868       ],
41869       [
41870         "Senegal (Sénégal)",
41871         "sn",
41872         "221"
41873       ],
41874       [
41875         "Serbia (Србија)",
41876         "rs",
41877         "381"
41878       ],
41879       [
41880         "Seychelles",
41881         "sc",
41882         "248"
41883       ],
41884       [
41885         "Sierra Leone",
41886         "sl",
41887         "232"
41888       ],
41889       [
41890         "Singapore",
41891         "sg",
41892         "65"
41893       ],
41894       [
41895         "Sint Maarten",
41896         "sx",
41897         "1721"
41898       ],
41899       [
41900         "Slovakia (Slovensko)",
41901         "sk",
41902         "421"
41903       ],
41904       [
41905         "Slovenia (Slovenija)",
41906         "si",
41907         "386"
41908       ],
41909       [
41910         "Solomon Islands",
41911         "sb",
41912         "677"
41913       ],
41914       [
41915         "Somalia (Soomaaliya)",
41916         "so",
41917         "252"
41918       ],
41919       [
41920         "South Africa",
41921         "za",
41922         "27"
41923       ],
41924       [
41925         "South Korea (대한민국)",
41926         "kr",
41927         "82"
41928       ],
41929       [
41930         "South Sudan (‫جنوب السودان‬‎)",
41931         "ss",
41932         "211"
41933       ],
41934       [
41935         "Spain (España)",
41936         "es",
41937         "34"
41938       ],
41939       [
41940         "Sri Lanka (ශ්‍රී ලංකාව)",
41941         "lk",
41942         "94"
41943       ],
41944       [
41945         "Sudan (‫السودان‬‎)",
41946         "sd",
41947         "249"
41948       ],
41949       [
41950         "Suriname",
41951         "sr",
41952         "597"
41953       ],
41954       [
41955         "Svalbard and Jan Mayen",
41956         "sj",
41957         "47",
41958         1
41959       ],
41960       [
41961         "Swaziland",
41962         "sz",
41963         "268"
41964       ],
41965       [
41966         "Sweden (Sverige)",
41967         "se",
41968         "46"
41969       ],
41970       [
41971         "Switzerland (Schweiz)",
41972         "ch",
41973         "41"
41974       ],
41975       [
41976         "Syria (‫سوريا‬‎)",
41977         "sy",
41978         "963"
41979       ],
41980       [
41981         "Taiwan (台灣)",
41982         "tw",
41983         "886"
41984       ],
41985       [
41986         "Tajikistan",
41987         "tj",
41988         "992"
41989       ],
41990       [
41991         "Tanzania",
41992         "tz",
41993         "255"
41994       ],
41995       [
41996         "Thailand (ไทย)",
41997         "th",
41998         "66"
41999       ],
42000       [
42001         "Timor-Leste",
42002         "tl",
42003         "670"
42004       ],
42005       [
42006         "Togo",
42007         "tg",
42008         "228"
42009       ],
42010       [
42011         "Tokelau",
42012         "tk",
42013         "690"
42014       ],
42015       [
42016         "Tonga",
42017         "to",
42018         "676"
42019       ],
42020       [
42021         "Trinidad and Tobago",
42022         "tt",
42023         "1868"
42024       ],
42025       [
42026         "Tunisia (‫تونس‬‎)",
42027         "tn",
42028         "216"
42029       ],
42030       [
42031         "Turkey (Türkiye)",
42032         "tr",
42033         "90"
42034       ],
42035       [
42036         "Turkmenistan",
42037         "tm",
42038         "993"
42039       ],
42040       [
42041         "Turks and Caicos Islands",
42042         "tc",
42043         "1649"
42044       ],
42045       [
42046         "Tuvalu",
42047         "tv",
42048         "688"
42049       ],
42050       [
42051         "U.S. Virgin Islands",
42052         "vi",
42053         "1340"
42054       ],
42055       [
42056         "Uganda",
42057         "ug",
42058         "256"
42059       ],
42060       [
42061         "Ukraine (Україна)",
42062         "ua",
42063         "380"
42064       ],
42065       [
42066         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42067         "ae",
42068         "971"
42069       ],
42070       [
42071         "United Kingdom",
42072         "gb",
42073         "44",
42074         0
42075       ],
42076       [
42077         "United States",
42078         "us",
42079         "1",
42080         0
42081       ],
42082       [
42083         "Uruguay",
42084         "uy",
42085         "598"
42086       ],
42087       [
42088         "Uzbekistan (Oʻzbekiston)",
42089         "uz",
42090         "998"
42091       ],
42092       [
42093         "Vanuatu",
42094         "vu",
42095         "678"
42096       ],
42097       [
42098         "Vatican City (Città del Vaticano)",
42099         "va",
42100         "39",
42101         1
42102       ],
42103       [
42104         "Venezuela",
42105         "ve",
42106         "58"
42107       ],
42108       [
42109         "Vietnam (Việt Nam)",
42110         "vn",
42111         "84"
42112       ],
42113       [
42114         "Wallis and Futuna (Wallis-et-Futuna)",
42115         "wf",
42116         "681"
42117       ],
42118       [
42119         "Western Sahara (‫الصحراء الغربية‬‎)",
42120         "eh",
42121         "212",
42122         1
42123       ],
42124       [
42125         "Yemen (‫اليمن‬‎)",
42126         "ye",
42127         "967"
42128       ],
42129       [
42130         "Zambia",
42131         "zm",
42132         "260"
42133       ],
42134       [
42135         "Zimbabwe",
42136         "zw",
42137         "263"
42138       ],
42139       [
42140         "Åland Islands",
42141         "ax",
42142         "358",
42143         1
42144       ]
42145   ];
42146   
42147   return d;
42148 }/**
42149 *    This script refer to:
42150 *    Title: International Telephone Input
42151 *    Author: Jack O'Connor
42152 *    Code version:  v12.1.12
42153 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42154 **/
42155
42156 /**
42157  * @class Roo.bootstrap.PhoneInput
42158  * @extends Roo.bootstrap.TriggerField
42159  * An input with International dial-code selection
42160  
42161  * @cfg {String} defaultDialCode default '+852'
42162  * @cfg {Array} preferedCountries default []
42163   
42164  * @constructor
42165  * Create a new PhoneInput.
42166  * @param {Object} config Configuration options
42167  */
42168
42169 Roo.bootstrap.PhoneInput = function(config) {
42170     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42171 };
42172
42173 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42174         
42175         listWidth: undefined,
42176         
42177         selectedClass: 'active',
42178         
42179         invalidClass : "has-warning",
42180         
42181         validClass: 'has-success',
42182         
42183         allowed: '0123456789',
42184         
42185         max_length: 15,
42186         
42187         /**
42188          * @cfg {String} defaultDialCode The default dial code when initializing the input
42189          */
42190         defaultDialCode: '+852',
42191         
42192         /**
42193          * @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
42194          */
42195         preferedCountries: false,
42196         
42197         getAutoCreate : function()
42198         {
42199             var data = Roo.bootstrap.PhoneInputData();
42200             var align = this.labelAlign || this.parentLabelAlign();
42201             var id = Roo.id();
42202             
42203             this.allCountries = [];
42204             this.dialCodeMapping = [];
42205             
42206             for (var i = 0; i < data.length; i++) {
42207               var c = data[i];
42208               this.allCountries[i] = {
42209                 name: c[0],
42210                 iso2: c[1],
42211                 dialCode: c[2],
42212                 priority: c[3] || 0,
42213                 areaCodes: c[4] || null
42214               };
42215               this.dialCodeMapping[c[2]] = {
42216                   name: c[0],
42217                   iso2: c[1],
42218                   priority: c[3] || 0,
42219                   areaCodes: c[4] || null
42220               };
42221             }
42222             
42223             var cfg = {
42224                 cls: 'form-group',
42225                 cn: []
42226             };
42227             
42228             var input =  {
42229                 tag: 'input',
42230                 id : id,
42231                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42232                 maxlength: this.max_length,
42233                 cls : 'form-control tel-input',
42234                 autocomplete: 'new-password'
42235             };
42236             
42237             var hiddenInput = {
42238                 tag: 'input',
42239                 type: 'hidden',
42240                 cls: 'hidden-tel-input'
42241             };
42242             
42243             if (this.name) {
42244                 hiddenInput.name = this.name;
42245             }
42246             
42247             if (this.disabled) {
42248                 input.disabled = true;
42249             }
42250             
42251             var flag_container = {
42252                 tag: 'div',
42253                 cls: 'flag-box',
42254                 cn: [
42255                     {
42256                         tag: 'div',
42257                         cls: 'flag'
42258                     },
42259                     {
42260                         tag: 'div',
42261                         cls: 'caret'
42262                     }
42263                 ]
42264             };
42265             
42266             var box = {
42267                 tag: 'div',
42268                 cls: this.hasFeedback ? 'has-feedback' : '',
42269                 cn: [
42270                     hiddenInput,
42271                     input,
42272                     {
42273                         tag: 'input',
42274                         cls: 'dial-code-holder',
42275                         disabled: true
42276                     }
42277                 ]
42278             };
42279             
42280             var container = {
42281                 cls: 'roo-select2-container input-group',
42282                 cn: [
42283                     flag_container,
42284                     box
42285                 ]
42286             };
42287             
42288             if (this.fieldLabel.length) {
42289                 var indicator = {
42290                     tag: 'i',
42291                     tooltip: 'This field is required'
42292                 };
42293                 
42294                 var label = {
42295                     tag: 'label',
42296                     'for':  id,
42297                     cls: 'control-label',
42298                     cn: []
42299                 };
42300                 
42301                 var label_text = {
42302                     tag: 'span',
42303                     html: this.fieldLabel
42304                 };
42305                 
42306                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42307                 label.cn = [
42308                     indicator,
42309                     label_text
42310                 ];
42311                 
42312                 if(this.indicatorpos == 'right') {
42313                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42314                     label.cn = [
42315                         label_text,
42316                         indicator
42317                     ];
42318                 }
42319                 
42320                 if(align == 'left') {
42321                     container = {
42322                         tag: 'div',
42323                         cn: [
42324                             container
42325                         ]
42326                     };
42327                     
42328                     if(this.labelWidth > 12){
42329                         label.style = "width: " + this.labelWidth + 'px';
42330                     }
42331                     if(this.labelWidth < 13 && this.labelmd == 0){
42332                         this.labelmd = this.labelWidth;
42333                     }
42334                     if(this.labellg > 0){
42335                         label.cls += ' col-lg-' + this.labellg;
42336                         input.cls += ' col-lg-' + (12 - this.labellg);
42337                     }
42338                     if(this.labelmd > 0){
42339                         label.cls += ' col-md-' + this.labelmd;
42340                         container.cls += ' col-md-' + (12 - this.labelmd);
42341                     }
42342                     if(this.labelsm > 0){
42343                         label.cls += ' col-sm-' + this.labelsm;
42344                         container.cls += ' col-sm-' + (12 - this.labelsm);
42345                     }
42346                     if(this.labelxs > 0){
42347                         label.cls += ' col-xs-' + this.labelxs;
42348                         container.cls += ' col-xs-' + (12 - this.labelxs);
42349                     }
42350                 }
42351             }
42352             
42353             cfg.cn = [
42354                 label,
42355                 container
42356             ];
42357             
42358             var settings = this;
42359             
42360             ['xs','sm','md','lg'].map(function(size){
42361                 if (settings[size]) {
42362                     cfg.cls += ' col-' + size + '-' + settings[size];
42363                 }
42364             });
42365             
42366             this.store = new Roo.data.Store({
42367                 proxy : new Roo.data.MemoryProxy({}),
42368                 reader : new Roo.data.JsonReader({
42369                     fields : [
42370                         {
42371                             'name' : 'name',
42372                             'type' : 'string'
42373                         },
42374                         {
42375                             'name' : 'iso2',
42376                             'type' : 'string'
42377                         },
42378                         {
42379                             'name' : 'dialCode',
42380                             'type' : 'string'
42381                         },
42382                         {
42383                             'name' : 'priority',
42384                             'type' : 'string'
42385                         },
42386                         {
42387                             'name' : 'areaCodes',
42388                             'type' : 'string'
42389                         }
42390                     ]
42391                 })
42392             });
42393             
42394             if(!this.preferedCountries) {
42395                 this.preferedCountries = [
42396                     'hk',
42397                     'gb',
42398                     'us'
42399                 ];
42400             }
42401             
42402             var p = this.preferedCountries.reverse();
42403             
42404             if(p) {
42405                 for (var i = 0; i < p.length; i++) {
42406                     for (var j = 0; j < this.allCountries.length; j++) {
42407                         if(this.allCountries[j].iso2 == p[i]) {
42408                             var t = this.allCountries[j];
42409                             this.allCountries.splice(j,1);
42410                             this.allCountries.unshift(t);
42411                         }
42412                     } 
42413                 }
42414             }
42415             
42416             this.store.proxy.data = {
42417                 success: true,
42418                 data: this.allCountries
42419             };
42420             
42421             return cfg;
42422         },
42423         
42424         initEvents : function()
42425         {
42426             this.createList();
42427             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42428             
42429             this.indicator = this.indicatorEl();
42430             this.flag = this.flagEl();
42431             this.dialCodeHolder = this.dialCodeHolderEl();
42432             
42433             this.trigger = this.el.select('div.flag-box',true).first();
42434             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42435             
42436             var _this = this;
42437             
42438             (function(){
42439                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42440                 _this.list.setWidth(lw);
42441             }).defer(100);
42442             
42443             this.list.on('mouseover', this.onViewOver, this);
42444             this.list.on('mousemove', this.onViewMove, this);
42445             this.inputEl().on("keyup", this.onKeyUp, this);
42446             this.inputEl().on("keypress", this.onKeyPress, this);
42447             
42448             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42449
42450             this.view = new Roo.View(this.list, this.tpl, {
42451                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42452             });
42453             
42454             this.view.on('click', this.onViewClick, this);
42455             this.setValue(this.defaultDialCode);
42456         },
42457         
42458         onTriggerClick : function(e)
42459         {
42460             Roo.log('trigger click');
42461             if(this.disabled){
42462                 return;
42463             }
42464             
42465             if(this.isExpanded()){
42466                 this.collapse();
42467                 this.hasFocus = false;
42468             }else {
42469                 this.store.load({});
42470                 this.hasFocus = true;
42471                 this.expand();
42472             }
42473         },
42474         
42475         isExpanded : function()
42476         {
42477             return this.list.isVisible();
42478         },
42479         
42480         collapse : function()
42481         {
42482             if(!this.isExpanded()){
42483                 return;
42484             }
42485             this.list.hide();
42486             Roo.get(document).un('mousedown', this.collapseIf, this);
42487             Roo.get(document).un('mousewheel', this.collapseIf, this);
42488             this.fireEvent('collapse', this);
42489             this.validate();
42490         },
42491         
42492         expand : function()
42493         {
42494             Roo.log('expand');
42495
42496             if(this.isExpanded() || !this.hasFocus){
42497                 return;
42498             }
42499             
42500             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42501             this.list.setWidth(lw);
42502             
42503             this.list.show();
42504             this.restrictHeight();
42505             
42506             Roo.get(document).on('mousedown', this.collapseIf, this);
42507             Roo.get(document).on('mousewheel', this.collapseIf, this);
42508             
42509             this.fireEvent('expand', this);
42510         },
42511         
42512         restrictHeight : function()
42513         {
42514             this.list.alignTo(this.inputEl(), this.listAlign);
42515             this.list.alignTo(this.inputEl(), this.listAlign);
42516         },
42517         
42518         onViewOver : function(e, t)
42519         {
42520             if(this.inKeyMode){
42521                 return;
42522             }
42523             var item = this.view.findItemFromChild(t);
42524             
42525             if(item){
42526                 var index = this.view.indexOf(item);
42527                 this.select(index, false);
42528             }
42529         },
42530
42531         // private
42532         onViewClick : function(view, doFocus, el, e)
42533         {
42534             var index = this.view.getSelectedIndexes()[0];
42535             
42536             var r = this.store.getAt(index);
42537             
42538             if(r){
42539                 this.onSelect(r, index);
42540             }
42541             if(doFocus !== false && !this.blockFocus){
42542                 this.inputEl().focus();
42543             }
42544         },
42545         
42546         onViewMove : function(e, t)
42547         {
42548             this.inKeyMode = false;
42549         },
42550         
42551         select : function(index, scrollIntoView)
42552         {
42553             this.selectedIndex = index;
42554             this.view.select(index);
42555             if(scrollIntoView !== false){
42556                 var el = this.view.getNode(index);
42557                 if(el){
42558                     this.list.scrollChildIntoView(el, false);
42559                 }
42560             }
42561         },
42562         
42563         createList : function()
42564         {
42565             this.list = Roo.get(document.body).createChild({
42566                 tag: 'ul',
42567                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42568                 style: 'display:none'
42569             });
42570             
42571             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42572         },
42573         
42574         collapseIf : function(e)
42575         {
42576             var in_combo  = e.within(this.el);
42577             var in_list =  e.within(this.list);
42578             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42579             
42580             if (in_combo || in_list || is_list) {
42581                 return;
42582             }
42583             this.collapse();
42584         },
42585         
42586         onSelect : function(record, index)
42587         {
42588             if(this.fireEvent('beforeselect', this, record, index) !== false){
42589                 
42590                 this.setFlagClass(record.data.iso2);
42591                 this.setDialCode(record.data.dialCode);
42592                 this.hasFocus = false;
42593                 this.collapse();
42594                 this.fireEvent('select', this, record, index);
42595             }
42596         },
42597         
42598         flagEl : function()
42599         {
42600             var flag = this.el.select('div.flag',true).first();
42601             if(!flag){
42602                 return false;
42603             }
42604             return flag;
42605         },
42606         
42607         dialCodeHolderEl : function()
42608         {
42609             var d = this.el.select('input.dial-code-holder',true).first();
42610             if(!d){
42611                 return false;
42612             }
42613             return d;
42614         },
42615         
42616         setDialCode : function(v)
42617         {
42618             this.dialCodeHolder.dom.value = '+'+v;
42619         },
42620         
42621         setFlagClass : function(n)
42622         {
42623             this.flag.dom.className = 'flag '+n;
42624         },
42625         
42626         getValue : function()
42627         {
42628             var v = this.inputEl().getValue();
42629             if(this.dialCodeHolder) {
42630                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42631             }
42632             return v;
42633         },
42634         
42635         setValue : function(v)
42636         {
42637             var d = this.getDialCode(v);
42638             
42639             //invalid dial code
42640             if(v.length == 0 || !d || d.length == 0) {
42641                 if(this.rendered){
42642                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42643                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42644                 }
42645                 return;
42646             }
42647             
42648             //valid dial code
42649             this.setFlagClass(this.dialCodeMapping[d].iso2);
42650             this.setDialCode(d);
42651             this.inputEl().dom.value = v.replace('+'+d,'');
42652             this.hiddenEl().dom.value = this.getValue();
42653             
42654             this.validate();
42655         },
42656         
42657         getDialCode : function(v)
42658         {
42659             v = v ||  '';
42660             
42661             if (v.length == 0) {
42662                 return this.dialCodeHolder.dom.value;
42663             }
42664             
42665             var dialCode = "";
42666             if (v.charAt(0) != "+") {
42667                 return false;
42668             }
42669             var numericChars = "";
42670             for (var i = 1; i < v.length; i++) {
42671               var c = v.charAt(i);
42672               if (!isNaN(c)) {
42673                 numericChars += c;
42674                 if (this.dialCodeMapping[numericChars]) {
42675                   dialCode = v.substr(1, i);
42676                 }
42677                 if (numericChars.length == 4) {
42678                   break;
42679                 }
42680               }
42681             }
42682             return dialCode;
42683         },
42684         
42685         reset : function()
42686         {
42687             this.setValue(this.defaultDialCode);
42688             this.validate();
42689         },
42690         
42691         hiddenEl : function()
42692         {
42693             return this.el.select('input.hidden-tel-input',true).first();
42694         },
42695         
42696         // after setting val
42697         onKeyUp : function(e){
42698             this.setValue(this.getValue());
42699         },
42700         
42701         onKeyPress : function(e){
42702             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42703                 e.stopEvent();
42704             }
42705         }
42706         
42707 });
42708 /**
42709  * @class Roo.bootstrap.MoneyField
42710  * @extends Roo.bootstrap.ComboBox
42711  * Bootstrap MoneyField class
42712  * 
42713  * @constructor
42714  * Create a new MoneyField.
42715  * @param {Object} config Configuration options
42716  */
42717
42718 Roo.bootstrap.MoneyField = function(config) {
42719     
42720     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42721     
42722 };
42723
42724 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42725     
42726     /**
42727      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42728      */
42729     allowDecimals : true,
42730     /**
42731      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42732      */
42733     decimalSeparator : ".",
42734     /**
42735      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42736      */
42737     decimalPrecision : 0,
42738     /**
42739      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42740      */
42741     allowNegative : true,
42742     /**
42743      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42744      */
42745     allowZero: true,
42746     /**
42747      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42748      */
42749     minValue : Number.NEGATIVE_INFINITY,
42750     /**
42751      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42752      */
42753     maxValue : Number.MAX_VALUE,
42754     /**
42755      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42756      */
42757     minText : "The minimum value for this field is {0}",
42758     /**
42759      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42760      */
42761     maxText : "The maximum value for this field is {0}",
42762     /**
42763      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42764      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42765      */
42766     nanText : "{0} is not a valid number",
42767     /**
42768      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42769      */
42770     castInt : true,
42771     /**
42772      * @cfg {String} defaults currency of the MoneyField
42773      * value should be in lkey
42774      */
42775     defaultCurrency : false,
42776     /**
42777      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42778      */
42779     thousandsDelimiter : false,
42780     /**
42781      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42782      */
42783     max_length: false,
42784     
42785     inputlg : 9,
42786     inputmd : 9,
42787     inputsm : 9,
42788     inputxs : 6,
42789     
42790     store : false,
42791     
42792     getAutoCreate : function()
42793     {
42794         var align = this.labelAlign || this.parentLabelAlign();
42795         
42796         var id = Roo.id();
42797
42798         var cfg = {
42799             cls: 'form-group',
42800             cn: []
42801         };
42802
42803         var input =  {
42804             tag: 'input',
42805             id : id,
42806             cls : 'form-control roo-money-amount-input',
42807             autocomplete: 'new-password'
42808         };
42809         
42810         var hiddenInput = {
42811             tag: 'input',
42812             type: 'hidden',
42813             id: Roo.id(),
42814             cls: 'hidden-number-input'
42815         };
42816         
42817         if(this.max_length) {
42818             input.maxlength = this.max_length; 
42819         }
42820         
42821         if (this.name) {
42822             hiddenInput.name = this.name;
42823         }
42824
42825         if (this.disabled) {
42826             input.disabled = true;
42827         }
42828
42829         var clg = 12 - this.inputlg;
42830         var cmd = 12 - this.inputmd;
42831         var csm = 12 - this.inputsm;
42832         var cxs = 12 - this.inputxs;
42833         
42834         var container = {
42835             tag : 'div',
42836             cls : 'row roo-money-field',
42837             cn : [
42838                 {
42839                     tag : 'div',
42840                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42841                     cn : [
42842                         {
42843                             tag : 'div',
42844                             cls: 'roo-select2-container input-group',
42845                             cn: [
42846                                 {
42847                                     tag : 'input',
42848                                     cls : 'form-control roo-money-currency-input',
42849                                     autocomplete: 'new-password',
42850                                     readOnly : 1,
42851                                     name : this.currencyName
42852                                 },
42853                                 {
42854                                     tag :'span',
42855                                     cls : 'input-group-addon',
42856                                     cn : [
42857                                         {
42858                                             tag: 'span',
42859                                             cls: 'caret'
42860                                         }
42861                                     ]
42862                                 }
42863                             ]
42864                         }
42865                     ]
42866                 },
42867                 {
42868                     tag : 'div',
42869                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42870                     cn : [
42871                         {
42872                             tag: 'div',
42873                             cls: this.hasFeedback ? 'has-feedback' : '',
42874                             cn: [
42875                                 input
42876                             ]
42877                         }
42878                     ]
42879                 }
42880             ]
42881             
42882         };
42883         
42884         if (this.fieldLabel.length) {
42885             var indicator = {
42886                 tag: 'i',
42887                 tooltip: 'This field is required'
42888             };
42889
42890             var label = {
42891                 tag: 'label',
42892                 'for':  id,
42893                 cls: 'control-label',
42894                 cn: []
42895             };
42896
42897             var label_text = {
42898                 tag: 'span',
42899                 html: this.fieldLabel
42900             };
42901
42902             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42903             label.cn = [
42904                 indicator,
42905                 label_text
42906             ];
42907
42908             if(this.indicatorpos == 'right') {
42909                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42910                 label.cn = [
42911                     label_text,
42912                     indicator
42913                 ];
42914             }
42915
42916             if(align == 'left') {
42917                 container = {
42918                     tag: 'div',
42919                     cn: [
42920                         container
42921                     ]
42922                 };
42923
42924                 if(this.labelWidth > 12){
42925                     label.style = "width: " + this.labelWidth + 'px';
42926                 }
42927                 if(this.labelWidth < 13 && this.labelmd == 0){
42928                     this.labelmd = this.labelWidth;
42929                 }
42930                 if(this.labellg > 0){
42931                     label.cls += ' col-lg-' + this.labellg;
42932                     input.cls += ' col-lg-' + (12 - this.labellg);
42933                 }
42934                 if(this.labelmd > 0){
42935                     label.cls += ' col-md-' + this.labelmd;
42936                     container.cls += ' col-md-' + (12 - this.labelmd);
42937                 }
42938                 if(this.labelsm > 0){
42939                     label.cls += ' col-sm-' + this.labelsm;
42940                     container.cls += ' col-sm-' + (12 - this.labelsm);
42941                 }
42942                 if(this.labelxs > 0){
42943                     label.cls += ' col-xs-' + this.labelxs;
42944                     container.cls += ' col-xs-' + (12 - this.labelxs);
42945                 }
42946             }
42947         }
42948
42949         cfg.cn = [
42950             label,
42951             container,
42952             hiddenInput
42953         ];
42954         
42955         var settings = this;
42956
42957         ['xs','sm','md','lg'].map(function(size){
42958             if (settings[size]) {
42959                 cfg.cls += ' col-' + size + '-' + settings[size];
42960             }
42961         });
42962         
42963         return cfg;
42964     },
42965     
42966     initEvents : function()
42967     {
42968         this.indicator = this.indicatorEl();
42969         
42970         this.initCurrencyEvent();
42971         
42972         this.initNumberEvent();
42973     },
42974     
42975     initCurrencyEvent : function()
42976     {
42977         if (!this.store) {
42978             throw "can not find store for combo";
42979         }
42980         
42981         this.store = Roo.factory(this.store, Roo.data);
42982         this.store.parent = this;
42983         
42984         this.createList();
42985         
42986         this.triggerEl = this.el.select('.input-group-addon', true).first();
42987         
42988         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42989         
42990         var _this = this;
42991         
42992         (function(){
42993             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42994             _this.list.setWidth(lw);
42995         }).defer(100);
42996         
42997         this.list.on('mouseover', this.onViewOver, this);
42998         this.list.on('mousemove', this.onViewMove, this);
42999         this.list.on('scroll', this.onViewScroll, this);
43000         
43001         if(!this.tpl){
43002             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43003         }
43004         
43005         this.view = new Roo.View(this.list, this.tpl, {
43006             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43007         });
43008         
43009         this.view.on('click', this.onViewClick, this);
43010         
43011         this.store.on('beforeload', this.onBeforeLoad, this);
43012         this.store.on('load', this.onLoad, this);
43013         this.store.on('loadexception', this.onLoadException, this);
43014         
43015         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43016             "up" : function(e){
43017                 this.inKeyMode = true;
43018                 this.selectPrev();
43019             },
43020
43021             "down" : function(e){
43022                 if(!this.isExpanded()){
43023                     this.onTriggerClick();
43024                 }else{
43025                     this.inKeyMode = true;
43026                     this.selectNext();
43027                 }
43028             },
43029
43030             "enter" : function(e){
43031                 this.collapse();
43032                 
43033                 if(this.fireEvent("specialkey", this, e)){
43034                     this.onViewClick(false);
43035                 }
43036                 
43037                 return true;
43038             },
43039
43040             "esc" : function(e){
43041                 this.collapse();
43042             },
43043
43044             "tab" : function(e){
43045                 this.collapse();
43046                 
43047                 if(this.fireEvent("specialkey", this, e)){
43048                     this.onViewClick(false);
43049                 }
43050                 
43051                 return true;
43052             },
43053
43054             scope : this,
43055
43056             doRelay : function(foo, bar, hname){
43057                 if(hname == 'down' || this.scope.isExpanded()){
43058                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43059                 }
43060                 return true;
43061             },
43062
43063             forceKeyDown: true
43064         });
43065         
43066         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43067         
43068     },
43069     
43070     initNumberEvent : function(e)
43071     {
43072         this.inputEl().on("keydown" , this.fireKey,  this);
43073         this.inputEl().on("focus", this.onFocus,  this);
43074         this.inputEl().on("blur", this.onBlur,  this);
43075         
43076         this.inputEl().relayEvent('keyup', this);
43077         
43078         if(this.indicator){
43079             this.indicator.addClass('invisible');
43080         }
43081  
43082         this.originalValue = this.getValue();
43083         
43084         if(this.validationEvent == 'keyup'){
43085             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43086             this.inputEl().on('keyup', this.filterValidation, this);
43087         }
43088         else if(this.validationEvent !== false){
43089             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43090         }
43091         
43092         if(this.selectOnFocus){
43093             this.on("focus", this.preFocus, this);
43094             
43095         }
43096         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43097             this.inputEl().on("keypress", this.filterKeys, this);
43098         } else {
43099             this.inputEl().relayEvent('keypress', this);
43100         }
43101         
43102         var allowed = "0123456789";
43103         
43104         if(this.allowDecimals){
43105             allowed += this.decimalSeparator;
43106         }
43107         
43108         if(this.allowNegative){
43109             allowed += "-";
43110         }
43111         
43112         if(this.thousandsDelimiter) {
43113             allowed += ",";
43114         }
43115         
43116         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43117         
43118         var keyPress = function(e){
43119             
43120             var k = e.getKey();
43121             
43122             var c = e.getCharCode();
43123             
43124             if(
43125                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43126                     allowed.indexOf(String.fromCharCode(c)) === -1
43127             ){
43128                 e.stopEvent();
43129                 return;
43130             }
43131             
43132             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43133                 return;
43134             }
43135             
43136             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43137                 e.stopEvent();
43138             }
43139         };
43140         
43141         this.inputEl().on("keypress", keyPress, this);
43142         
43143     },
43144     
43145     onTriggerClick : function(e)
43146     {   
43147         if(this.disabled){
43148             return;
43149         }
43150         
43151         this.page = 0;
43152         this.loadNext = false;
43153         
43154         if(this.isExpanded()){
43155             this.collapse();
43156             return;
43157         }
43158         
43159         this.hasFocus = true;
43160         
43161         if(this.triggerAction == 'all') {
43162             this.doQuery(this.allQuery, true);
43163             return;
43164         }
43165         
43166         this.doQuery(this.getRawValue());
43167     },
43168     
43169     getCurrency : function()
43170     {   
43171         var v = this.currencyEl().getValue();
43172         
43173         return v;
43174     },
43175     
43176     restrictHeight : function()
43177     {
43178         this.list.alignTo(this.currencyEl(), this.listAlign);
43179         this.list.alignTo(this.currencyEl(), this.listAlign);
43180     },
43181     
43182     onViewClick : function(view, doFocus, el, e)
43183     {
43184         var index = this.view.getSelectedIndexes()[0];
43185         
43186         var r = this.store.getAt(index);
43187         
43188         if(r){
43189             this.onSelect(r, index);
43190         }
43191     },
43192     
43193     onSelect : function(record, index){
43194         
43195         if(this.fireEvent('beforeselect', this, record, index) !== false){
43196         
43197             this.setFromCurrencyData(index > -1 ? record.data : false);
43198             
43199             this.collapse();
43200             
43201             this.fireEvent('select', this, record, index);
43202         }
43203     },
43204     
43205     setFromCurrencyData : function(o)
43206     {
43207         var currency = '';
43208         
43209         this.lastCurrency = o;
43210         
43211         if (this.currencyField) {
43212             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43213         } else {
43214             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43215         }
43216         
43217         this.lastSelectionText = currency;
43218         
43219         //setting default currency
43220         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43221             this.setCurrency(this.defaultCurrency);
43222             return;
43223         }
43224         
43225         this.setCurrency(currency);
43226     },
43227     
43228     setFromData : function(o)
43229     {
43230         var c = {};
43231         
43232         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43233         
43234         this.setFromCurrencyData(c);
43235         
43236         var value = '';
43237         
43238         if (this.name) {
43239             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43240         } else {
43241             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43242         }
43243         
43244         this.setValue(value);
43245         
43246     },
43247     
43248     setCurrency : function(v)
43249     {   
43250         this.currencyValue = v;
43251         
43252         if(this.rendered){
43253             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43254             this.validate();
43255         }
43256     },
43257     
43258     setValue : function(v)
43259     {
43260         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43261         
43262         this.value = v;
43263         
43264         if(this.rendered){
43265             
43266             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43267             
43268             this.inputEl().dom.value = (v == '') ? '' :
43269                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43270             
43271             if(!this.allowZero && v === '0') {
43272                 this.hiddenEl().dom.value = '';
43273                 this.inputEl().dom.value = '';
43274             }
43275             
43276             this.validate();
43277         }
43278     },
43279     
43280     getRawValue : function()
43281     {
43282         var v = this.inputEl().getValue();
43283         
43284         return v;
43285     },
43286     
43287     getValue : function()
43288     {
43289         return this.fixPrecision(this.parseValue(this.getRawValue()));
43290     },
43291     
43292     parseValue : function(value)
43293     {
43294         if(this.thousandsDelimiter) {
43295             value += "";
43296             r = new RegExp(",", "g");
43297             value = value.replace(r, "");
43298         }
43299         
43300         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43301         return isNaN(value) ? '' : value;
43302         
43303     },
43304     
43305     fixPrecision : function(value)
43306     {
43307         if(this.thousandsDelimiter) {
43308             value += "";
43309             r = new RegExp(",", "g");
43310             value = value.replace(r, "");
43311         }
43312         
43313         var nan = isNaN(value);
43314         
43315         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43316             return nan ? '' : value;
43317         }
43318         return parseFloat(value).toFixed(this.decimalPrecision);
43319     },
43320     
43321     decimalPrecisionFcn : function(v)
43322     {
43323         return Math.floor(v);
43324     },
43325     
43326     validateValue : function(value)
43327     {
43328         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43329             return false;
43330         }
43331         
43332         var num = this.parseValue(value);
43333         
43334         if(isNaN(num)){
43335             this.markInvalid(String.format(this.nanText, value));
43336             return false;
43337         }
43338         
43339         if(num < this.minValue){
43340             this.markInvalid(String.format(this.minText, this.minValue));
43341             return false;
43342         }
43343         
43344         if(num > this.maxValue){
43345             this.markInvalid(String.format(this.maxText, this.maxValue));
43346             return false;
43347         }
43348         
43349         return true;
43350     },
43351     
43352     validate : function()
43353     {
43354         if(this.disabled || this.allowBlank){
43355             this.markValid();
43356             return true;
43357         }
43358         
43359         var currency = this.getCurrency();
43360         
43361         if(this.validateValue(this.getRawValue()) && currency.length){
43362             this.markValid();
43363             return true;
43364         }
43365         
43366         this.markInvalid();
43367         return false;
43368     },
43369     
43370     getName: function()
43371     {
43372         return this.name;
43373     },
43374     
43375     beforeBlur : function()
43376     {
43377         if(!this.castInt){
43378             return;
43379         }
43380         
43381         var v = this.parseValue(this.getRawValue());
43382         
43383         if(v || v == 0){
43384             this.setValue(v);
43385         }
43386     },
43387     
43388     onBlur : function()
43389     {
43390         this.beforeBlur();
43391         
43392         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43393             //this.el.removeClass(this.focusClass);
43394         }
43395         
43396         this.hasFocus = false;
43397         
43398         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43399             this.validate();
43400         }
43401         
43402         var v = this.getValue();
43403         
43404         if(String(v) !== String(this.startValue)){
43405             this.fireEvent('change', this, v, this.startValue);
43406         }
43407         
43408         this.fireEvent("blur", this);
43409     },
43410     
43411     inputEl : function()
43412     {
43413         return this.el.select('.roo-money-amount-input', true).first();
43414     },
43415     
43416     currencyEl : function()
43417     {
43418         return this.el.select('.roo-money-currency-input', true).first();
43419     },
43420     
43421     hiddenEl : function()
43422     {
43423         return this.el.select('input.hidden-number-input',true).first();
43424     }
43425     
43426 });/**
43427  * @class Roo.bootstrap.BezierSignature
43428  * @extends Roo.bootstrap.Component
43429  * Bootstrap BezierSignature class
43430  * This script refer to:
43431  *    Title: Signature Pad
43432  *    Author: szimek
43433  *    Availability: https://github.com/szimek/signature_pad
43434  *
43435  * @constructor
43436  * Create a new BezierSignature
43437  * @param {Object} config The config object
43438  */
43439
43440 Roo.bootstrap.BezierSignature = function(config){
43441     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43442     this.addEvents({
43443         "resize" : true
43444     });
43445 };
43446
43447 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43448 {
43449      
43450     curve_data: [],
43451     
43452     is_empty: true,
43453     
43454     mouse_btn_down: true,
43455     
43456     /**
43457      * @cfg {int} canvas height
43458      */
43459     canvas_height: '200px',
43460     
43461     /**
43462      * @cfg {float|function} Radius of a single dot.
43463      */ 
43464     dot_size: false,
43465     
43466     /**
43467      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43468      */
43469     min_width: 0.5,
43470     
43471     /**
43472      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43473      */
43474     max_width: 2.5,
43475     
43476     /**
43477      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43478      */
43479     throttle: 16,
43480     
43481     /**
43482      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43483      */
43484     min_distance: 5,
43485     
43486     /**
43487      * @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.
43488      */
43489     bg_color: 'rgba(0, 0, 0, 0)',
43490     
43491     /**
43492      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43493      */
43494     dot_color: 'black',
43495     
43496     /**
43497      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43498      */ 
43499     velocity_filter_weight: 0.7,
43500     
43501     /**
43502      * @cfg {function} Callback when stroke begin. 
43503      */
43504     onBegin: false,
43505     
43506     /**
43507      * @cfg {function} Callback when stroke end.
43508      */
43509     onEnd: false,
43510     
43511     getAutoCreate : function()
43512     {
43513         var cls = 'roo-signature column';
43514         
43515         if(this.cls){
43516             cls += ' ' + this.cls;
43517         }
43518         
43519         var col_sizes = [
43520             'lg',
43521             'md',
43522             'sm',
43523             'xs'
43524         ];
43525         
43526         for(var i = 0; i < col_sizes.length; i++) {
43527             if(this[col_sizes[i]]) {
43528                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43529             }
43530         }
43531         
43532         var cfg = {
43533             tag: 'div',
43534             cls: cls,
43535             cn: [
43536                 {
43537                     tag: 'div',
43538                     cls: 'roo-signature-body',
43539                     cn: [
43540                         {
43541                             tag: 'canvas',
43542                             cls: 'roo-signature-body-canvas',
43543                             height: this.canvas_height,
43544                             width: this.canvas_width
43545                         }
43546                     ]
43547                 },
43548                 {
43549                     tag: 'input',
43550                     type: 'file',
43551                     style: 'display: none'
43552                 }
43553             ]
43554         };
43555         
43556         return cfg;
43557     },
43558     
43559     initEvents: function() 
43560     {
43561         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43562         
43563         var canvas = this.canvasEl();
43564         
43565         // mouse && touch event swapping...
43566         canvas.dom.style.touchAction = 'none';
43567         canvas.dom.style.msTouchAction = 'none';
43568         
43569         this.mouse_btn_down = false;
43570         canvas.on('mousedown', this._handleMouseDown, this);
43571         canvas.on('mousemove', this._handleMouseMove, this);
43572         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43573         
43574         if (window.PointerEvent) {
43575             canvas.on('pointerdown', this._handleMouseDown, this);
43576             canvas.on('pointermove', this._handleMouseMove, this);
43577             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43578         }
43579         
43580         if ('ontouchstart' in window) {
43581             canvas.on('touchstart', this._handleTouchStart, this);
43582             canvas.on('touchmove', this._handleTouchMove, this);
43583             canvas.on('touchend', this._handleTouchEnd, this);
43584         }
43585         
43586         Roo.EventManager.onWindowResize(this.resize, this, true);
43587         
43588         // file input event
43589         this.fileEl().on('change', this.uploadImage, this);
43590         
43591         this.clear();
43592         
43593         this.resize();
43594     },
43595     
43596     resize: function(){
43597         
43598         var canvas = this.canvasEl().dom;
43599         var ctx = this.canvasElCtx();
43600         var img_data = false;
43601         
43602         if(canvas.width > 0) {
43603             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43604         }
43605         // setting canvas width will clean img data
43606         canvas.width = 0;
43607         
43608         var style = window.getComputedStyle ? 
43609             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43610             
43611         var padding_left = parseInt(style.paddingLeft) || 0;
43612         var padding_right = parseInt(style.paddingRight) || 0;
43613         
43614         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43615         
43616         if(img_data) {
43617             ctx.putImageData(img_data, 0, 0);
43618         }
43619     },
43620     
43621     _handleMouseDown: function(e)
43622     {
43623         if (e.browserEvent.which === 1) {
43624             this.mouse_btn_down = true;
43625             this.strokeBegin(e);
43626         }
43627     },
43628     
43629     _handleMouseMove: function (e)
43630     {
43631         if (this.mouse_btn_down) {
43632             this.strokeMoveUpdate(e);
43633         }
43634     },
43635     
43636     _handleMouseUp: function (e)
43637     {
43638         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43639             this.mouse_btn_down = false;
43640             this.strokeEnd(e);
43641         }
43642     },
43643     
43644     _handleTouchStart: function (e) {
43645         
43646         e.preventDefault();
43647         if (e.browserEvent.targetTouches.length === 1) {
43648             // var touch = e.browserEvent.changedTouches[0];
43649             // this.strokeBegin(touch);
43650             
43651              this.strokeBegin(e); // assume e catching the correct xy...
43652         }
43653     },
43654     
43655     _handleTouchMove: function (e) {
43656         e.preventDefault();
43657         // var touch = event.targetTouches[0];
43658         // _this._strokeMoveUpdate(touch);
43659         this.strokeMoveUpdate(e);
43660     },
43661     
43662     _handleTouchEnd: function (e) {
43663         var wasCanvasTouched = e.target === this.canvasEl().dom;
43664         if (wasCanvasTouched) {
43665             e.preventDefault();
43666             // var touch = event.changedTouches[0];
43667             // _this._strokeEnd(touch);
43668             this.strokeEnd(e);
43669         }
43670     },
43671     
43672     reset: function () {
43673         this._lastPoints = [];
43674         this._lastVelocity = 0;
43675         this._lastWidth = (this.min_width + this.max_width) / 2;
43676         this.canvasElCtx().fillStyle = this.dot_color;
43677     },
43678     
43679     strokeMoveUpdate: function(e)
43680     {
43681         this.strokeUpdate(e);
43682         
43683         if (this.throttle) {
43684             this.throttleStroke(this.strokeUpdate, this.throttle);
43685         }
43686         else {
43687             this.strokeUpdate(e);
43688         }
43689     },
43690     
43691     strokeBegin: function(e)
43692     {
43693         var newPointGroup = {
43694             color: this.dot_color,
43695             points: []
43696         };
43697         
43698         if (typeof this.onBegin === 'function') {
43699             this.onBegin(e);
43700         }
43701         
43702         this.curve_data.push(newPointGroup);
43703         this.reset();
43704         this.strokeUpdate(e);
43705     },
43706     
43707     strokeUpdate: function(e)
43708     {
43709         var rect = this.canvasEl().dom.getBoundingClientRect();
43710         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43711         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43712         var lastPoints = lastPointGroup.points;
43713         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43714         var isLastPointTooClose = lastPoint
43715             ? point.distanceTo(lastPoint) <= this.min_distance
43716             : false;
43717         var color = lastPointGroup.color;
43718         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43719             var curve = this.addPoint(point);
43720             if (!lastPoint) {
43721                 this.drawDot({color: color, point: point});
43722             }
43723             else if (curve) {
43724                 this.drawCurve({color: color, curve: curve});
43725             }
43726             lastPoints.push({
43727                 time: point.time,
43728                 x: point.x,
43729                 y: point.y
43730             });
43731         }
43732     },
43733     
43734     strokeEnd: function(e)
43735     {
43736         this.strokeUpdate(e);
43737         if (typeof this.onEnd === 'function') {
43738             this.onEnd(e);
43739         }
43740     },
43741     
43742     addPoint:  function (point) {
43743         var _lastPoints = this._lastPoints;
43744         _lastPoints.push(point);
43745         if (_lastPoints.length > 2) {
43746             if (_lastPoints.length === 3) {
43747                 _lastPoints.unshift(_lastPoints[0]);
43748             }
43749             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43750             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43751             _lastPoints.shift();
43752             return curve;
43753         }
43754         return null;
43755     },
43756     
43757     calculateCurveWidths: function (startPoint, endPoint) {
43758         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43759             (1 - this.velocity_filter_weight) * this._lastVelocity;
43760
43761         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43762         var widths = {
43763             end: newWidth,
43764             start: this._lastWidth
43765         };
43766         
43767         this._lastVelocity = velocity;
43768         this._lastWidth = newWidth;
43769         return widths;
43770     },
43771     
43772     drawDot: function (_a) {
43773         var color = _a.color, point = _a.point;
43774         var ctx = this.canvasElCtx();
43775         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43776         ctx.beginPath();
43777         this.drawCurveSegment(point.x, point.y, width);
43778         ctx.closePath();
43779         ctx.fillStyle = color;
43780         ctx.fill();
43781     },
43782     
43783     drawCurve: function (_a) {
43784         var color = _a.color, curve = _a.curve;
43785         var ctx = this.canvasElCtx();
43786         var widthDelta = curve.endWidth - curve.startWidth;
43787         var drawSteps = Math.floor(curve.length()) * 2;
43788         ctx.beginPath();
43789         ctx.fillStyle = color;
43790         for (var i = 0; i < drawSteps; i += 1) {
43791         var t = i / drawSteps;
43792         var tt = t * t;
43793         var ttt = tt * t;
43794         var u = 1 - t;
43795         var uu = u * u;
43796         var uuu = uu * u;
43797         var x = uuu * curve.startPoint.x;
43798         x += 3 * uu * t * curve.control1.x;
43799         x += 3 * u * tt * curve.control2.x;
43800         x += ttt * curve.endPoint.x;
43801         var y = uuu * curve.startPoint.y;
43802         y += 3 * uu * t * curve.control1.y;
43803         y += 3 * u * tt * curve.control2.y;
43804         y += ttt * curve.endPoint.y;
43805         var width = curve.startWidth + ttt * widthDelta;
43806         this.drawCurveSegment(x, y, width);
43807         }
43808         ctx.closePath();
43809         ctx.fill();
43810     },
43811     
43812     drawCurveSegment: function (x, y, width) {
43813         var ctx = this.canvasElCtx();
43814         ctx.moveTo(x, y);
43815         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43816         this.is_empty = false;
43817     },
43818     
43819     clear: function()
43820     {
43821         var ctx = this.canvasElCtx();
43822         var canvas = this.canvasEl().dom;
43823         ctx.fillStyle = this.bg_color;
43824         ctx.clearRect(0, 0, canvas.width, canvas.height);
43825         ctx.fillRect(0, 0, canvas.width, canvas.height);
43826         this.curve_data = [];
43827         this.reset();
43828         this.is_empty = true;
43829     },
43830     
43831     fileEl: function()
43832     {
43833         return  this.el.select('input',true).first();
43834     },
43835     
43836     canvasEl: function()
43837     {
43838         return this.el.select('canvas',true).first();
43839     },
43840     
43841     canvasElCtx: function()
43842     {
43843         return this.el.select('canvas',true).first().dom.getContext('2d');
43844     },
43845     
43846     getImage: function(type)
43847     {
43848         if(this.is_empty) {
43849             return false;
43850         }
43851         
43852         // encryption ?
43853         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43854     },
43855     
43856     drawFromImage: function(img_src)
43857     {
43858         var img = new Image();
43859         
43860         img.onload = function(){
43861             this.canvasElCtx().drawImage(img, 0, 0);
43862         }.bind(this);
43863         
43864         img.src = img_src;
43865         
43866         this.is_empty = false;
43867     },
43868     
43869     selectImage: function()
43870     {
43871         this.fileEl().dom.click();
43872     },
43873     
43874     uploadImage: function(e)
43875     {
43876         var reader = new FileReader();
43877         
43878         reader.onload = function(e){
43879             var img = new Image();
43880             img.onload = function(){
43881                 this.reset();
43882                 this.canvasElCtx().drawImage(img, 0, 0);
43883             }.bind(this);
43884             img.src = e.target.result;
43885         }.bind(this);
43886         
43887         reader.readAsDataURL(e.target.files[0]);
43888     },
43889     
43890     // Bezier Point Constructor
43891     Point: (function () {
43892         function Point(x, y, time) {
43893             this.x = x;
43894             this.y = y;
43895             this.time = time || Date.now();
43896         }
43897         Point.prototype.distanceTo = function (start) {
43898             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43899         };
43900         Point.prototype.equals = function (other) {
43901             return this.x === other.x && this.y === other.y && this.time === other.time;
43902         };
43903         Point.prototype.velocityFrom = function (start) {
43904             return this.time !== start.time
43905             ? this.distanceTo(start) / (this.time - start.time)
43906             : 0;
43907         };
43908         return Point;
43909     }()),
43910     
43911     
43912     // Bezier Constructor
43913     Bezier: (function () {
43914         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43915             this.startPoint = startPoint;
43916             this.control2 = control2;
43917             this.control1 = control1;
43918             this.endPoint = endPoint;
43919             this.startWidth = startWidth;
43920             this.endWidth = endWidth;
43921         }
43922         Bezier.fromPoints = function (points, widths, scope) {
43923             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43924             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43925             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43926         };
43927         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43928             var dx1 = s1.x - s2.x;
43929             var dy1 = s1.y - s2.y;
43930             var dx2 = s2.x - s3.x;
43931             var dy2 = s2.y - s3.y;
43932             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43933             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43934             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43935             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43936             var dxm = m1.x - m2.x;
43937             var dym = m1.y - m2.y;
43938             var k = l2 / (l1 + l2);
43939             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43940             var tx = s2.x - cm.x;
43941             var ty = s2.y - cm.y;
43942             return {
43943                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43944                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43945             };
43946         };
43947         Bezier.prototype.length = function () {
43948             var steps = 10;
43949             var length = 0;
43950             var px;
43951             var py;
43952             for (var i = 0; i <= steps; i += 1) {
43953                 var t = i / steps;
43954                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43955                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43956                 if (i > 0) {
43957                     var xdiff = cx - px;
43958                     var ydiff = cy - py;
43959                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43960                 }
43961                 px = cx;
43962                 py = cy;
43963             }
43964             return length;
43965         };
43966         Bezier.prototype.point = function (t, start, c1, c2, end) {
43967             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43968             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43969             + (3.0 * c2 * (1.0 - t) * t * t)
43970             + (end * t * t * t);
43971         };
43972         return Bezier;
43973     }()),
43974     
43975     throttleStroke: function(fn, wait) {
43976       if (wait === void 0) { wait = 250; }
43977       var previous = 0;
43978       var timeout = null;
43979       var result;
43980       var storedContext;
43981       var storedArgs;
43982       var later = function () {
43983           previous = Date.now();
43984           timeout = null;
43985           result = fn.apply(storedContext, storedArgs);
43986           if (!timeout) {
43987               storedContext = null;
43988               storedArgs = [];
43989           }
43990       };
43991       return function wrapper() {
43992           var args = [];
43993           for (var _i = 0; _i < arguments.length; _i++) {
43994               args[_i] = arguments[_i];
43995           }
43996           var now = Date.now();
43997           var remaining = wait - (now - previous);
43998           storedContext = this;
43999           storedArgs = args;
44000           if (remaining <= 0 || remaining > wait) {
44001               if (timeout) {
44002                   clearTimeout(timeout);
44003                   timeout = null;
44004               }
44005               previous = now;
44006               result = fn.apply(storedContext, storedArgs);
44007               if (!timeout) {
44008                   storedContext = null;
44009                   storedArgs = [];
44010               }
44011           }
44012           else if (!timeout) {
44013               timeout = window.setTimeout(later, remaining);
44014           }
44015           return result;
44016       };
44017   }
44018   
44019 });
44020
44021  
44022
44023