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         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26146         this.markdownEl.on('click', this.toggleTextEdit, this);
26147         this.on('blur', this.toggleTextEdit, this);
26148         this.on('specialkey', this.resizeTextArea, this);
26149     },
26150     
26151     toggleTextEdit : function()
26152     {
26153         var sh = this.markdownEl.getHeight();
26154         this.inputEl().addClass('d-none');
26155         this.markdownEl.addClass('d-none');
26156         if (!this.editing) {
26157             // show editor?
26158             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26159             this.inputEl().removeClass('d-none');
26160             this.inputEl().focus();
26161             this.editing = true;
26162             return;
26163         }
26164         // show showdown...
26165         this.updateMarkdown();
26166         this.markdownEl.removeClass('d-none');
26167         this.editing = false;
26168         return;
26169     },
26170     updateMarkdown : function()
26171     {
26172         if (this.getValue() == '') {
26173             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder);
26174             return;
26175         }
26176         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26177     },
26178     
26179     resizeTextArea: function () {
26180         
26181         var sh = 100;
26182         Roo.log([sh, this.getValue().split("\n").length * 30]);
26183         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26184     },
26185     setValue : function(val)
26186     {
26187         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26188         if (!this.editing) {
26189             this.updateMarkdown();
26190         }
26191         
26192     },
26193     focus : function()
26194     {
26195         if (!this.editing) {
26196             this.toggleTextEdit();
26197         }
26198         
26199     }
26200
26201
26202 });
26203 /**
26204  * @class Roo.bootstrap.Table.AbstractSelectionModel
26205  * @extends Roo.util.Observable
26206  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26207  * implemented by descendant classes.  This class should not be directly instantiated.
26208  * @constructor
26209  */
26210 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26211     this.locked = false;
26212     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26213 };
26214
26215
26216 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26217     /** @ignore Called by the grid automatically. Do not call directly. */
26218     init : function(grid){
26219         this.grid = grid;
26220         this.initEvents();
26221     },
26222
26223     /**
26224      * Locks the selections.
26225      */
26226     lock : function(){
26227         this.locked = true;
26228     },
26229
26230     /**
26231      * Unlocks the selections.
26232      */
26233     unlock : function(){
26234         this.locked = false;
26235     },
26236
26237     /**
26238      * Returns true if the selections are locked.
26239      * @return {Boolean}
26240      */
26241     isLocked : function(){
26242         return this.locked;
26243     },
26244     
26245     
26246     initEvents : function ()
26247     {
26248         
26249     }
26250 });
26251 /**
26252  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26253  * @class Roo.bootstrap.Table.RowSelectionModel
26254  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26255  * It supports multiple selections and keyboard selection/navigation. 
26256  * @constructor
26257  * @param {Object} config
26258  */
26259
26260 Roo.bootstrap.Table.RowSelectionModel = function(config){
26261     Roo.apply(this, config);
26262     this.selections = new Roo.util.MixedCollection(false, function(o){
26263         return o.id;
26264     });
26265
26266     this.last = false;
26267     this.lastActive = false;
26268
26269     this.addEvents({
26270         /**
26271              * @event selectionchange
26272              * Fires when the selection changes
26273              * @param {SelectionModel} this
26274              */
26275             "selectionchange" : true,
26276         /**
26277              * @event afterselectionchange
26278              * Fires after the selection changes (eg. by key press or clicking)
26279              * @param {SelectionModel} this
26280              */
26281             "afterselectionchange" : true,
26282         /**
26283              * @event beforerowselect
26284              * Fires when a row is selected being selected, return false to cancel.
26285              * @param {SelectionModel} this
26286              * @param {Number} rowIndex The selected index
26287              * @param {Boolean} keepExisting False if other selections will be cleared
26288              */
26289             "beforerowselect" : true,
26290         /**
26291              * @event rowselect
26292              * Fires when a row is selected.
26293              * @param {SelectionModel} this
26294              * @param {Number} rowIndex The selected index
26295              * @param {Roo.data.Record} r The record
26296              */
26297             "rowselect" : true,
26298         /**
26299              * @event rowdeselect
26300              * Fires when a row is deselected.
26301              * @param {SelectionModel} this
26302              * @param {Number} rowIndex The selected index
26303              */
26304         "rowdeselect" : true
26305     });
26306     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26307     this.locked = false;
26308  };
26309
26310 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26311     /**
26312      * @cfg {Boolean} singleSelect
26313      * True to allow selection of only one row at a time (defaults to false)
26314      */
26315     singleSelect : false,
26316
26317     // private
26318     initEvents : function()
26319     {
26320
26321         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26322         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26323         //}else{ // allow click to work like normal
26324          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26325         //}
26326         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26327         this.grid.on("rowclick", this.handleMouseDown, this);
26328         
26329         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26330             "up" : function(e){
26331                 if(!e.shiftKey){
26332                     this.selectPrevious(e.shiftKey);
26333                 }else if(this.last !== false && this.lastActive !== false){
26334                     var last = this.last;
26335                     this.selectRange(this.last,  this.lastActive-1);
26336                     this.grid.getView().focusRow(this.lastActive);
26337                     if(last !== false){
26338                         this.last = last;
26339                     }
26340                 }else{
26341                     this.selectFirstRow();
26342                 }
26343                 this.fireEvent("afterselectionchange", this);
26344             },
26345             "down" : function(e){
26346                 if(!e.shiftKey){
26347                     this.selectNext(e.shiftKey);
26348                 }else if(this.last !== false && this.lastActive !== false){
26349                     var last = this.last;
26350                     this.selectRange(this.last,  this.lastActive+1);
26351                     this.grid.getView().focusRow(this.lastActive);
26352                     if(last !== false){
26353                         this.last = last;
26354                     }
26355                 }else{
26356                     this.selectFirstRow();
26357                 }
26358                 this.fireEvent("afterselectionchange", this);
26359             },
26360             scope: this
26361         });
26362         this.grid.store.on('load', function(){
26363             this.selections.clear();
26364         },this);
26365         /*
26366         var view = this.grid.view;
26367         view.on("refresh", this.onRefresh, this);
26368         view.on("rowupdated", this.onRowUpdated, this);
26369         view.on("rowremoved", this.onRemove, this);
26370         */
26371     },
26372
26373     // private
26374     onRefresh : function()
26375     {
26376         var ds = this.grid.store, i, v = this.grid.view;
26377         var s = this.selections;
26378         s.each(function(r){
26379             if((i = ds.indexOfId(r.id)) != -1){
26380                 v.onRowSelect(i);
26381             }else{
26382                 s.remove(r);
26383             }
26384         });
26385     },
26386
26387     // private
26388     onRemove : function(v, index, r){
26389         this.selections.remove(r);
26390     },
26391
26392     // private
26393     onRowUpdated : function(v, index, r){
26394         if(this.isSelected(r)){
26395             v.onRowSelect(index);
26396         }
26397     },
26398
26399     /**
26400      * Select records.
26401      * @param {Array} records The records to select
26402      * @param {Boolean} keepExisting (optional) True to keep existing selections
26403      */
26404     selectRecords : function(records, keepExisting)
26405     {
26406         if(!keepExisting){
26407             this.clearSelections();
26408         }
26409             var ds = this.grid.store;
26410         for(var i = 0, len = records.length; i < len; i++){
26411             this.selectRow(ds.indexOf(records[i]), true);
26412         }
26413     },
26414
26415     /**
26416      * Gets the number of selected rows.
26417      * @return {Number}
26418      */
26419     getCount : function(){
26420         return this.selections.length;
26421     },
26422
26423     /**
26424      * Selects the first row in the grid.
26425      */
26426     selectFirstRow : function(){
26427         this.selectRow(0);
26428     },
26429
26430     /**
26431      * Select the last row.
26432      * @param {Boolean} keepExisting (optional) True to keep existing selections
26433      */
26434     selectLastRow : function(keepExisting){
26435         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26436         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26437     },
26438
26439     /**
26440      * Selects the row immediately following the last selected row.
26441      * @param {Boolean} keepExisting (optional) True to keep existing selections
26442      */
26443     selectNext : function(keepExisting)
26444     {
26445             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26446             this.selectRow(this.last+1, keepExisting);
26447             this.grid.getView().focusRow(this.last);
26448         }
26449     },
26450
26451     /**
26452      * Selects the row that precedes the last selected row.
26453      * @param {Boolean} keepExisting (optional) True to keep existing selections
26454      */
26455     selectPrevious : function(keepExisting){
26456         if(this.last){
26457             this.selectRow(this.last-1, keepExisting);
26458             this.grid.getView().focusRow(this.last);
26459         }
26460     },
26461
26462     /**
26463      * Returns the selected records
26464      * @return {Array} Array of selected records
26465      */
26466     getSelections : function(){
26467         return [].concat(this.selections.items);
26468     },
26469
26470     /**
26471      * Returns the first selected record.
26472      * @return {Record}
26473      */
26474     getSelected : function(){
26475         return this.selections.itemAt(0);
26476     },
26477
26478
26479     /**
26480      * Clears all selections.
26481      */
26482     clearSelections : function(fast)
26483     {
26484         if(this.locked) {
26485             return;
26486         }
26487         if(fast !== true){
26488                 var ds = this.grid.store;
26489             var s = this.selections;
26490             s.each(function(r){
26491                 this.deselectRow(ds.indexOfId(r.id));
26492             }, this);
26493             s.clear();
26494         }else{
26495             this.selections.clear();
26496         }
26497         this.last = false;
26498     },
26499
26500
26501     /**
26502      * Selects all rows.
26503      */
26504     selectAll : function(){
26505         if(this.locked) {
26506             return;
26507         }
26508         this.selections.clear();
26509         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26510             this.selectRow(i, true);
26511         }
26512     },
26513
26514     /**
26515      * Returns True if there is a selection.
26516      * @return {Boolean}
26517      */
26518     hasSelection : function(){
26519         return this.selections.length > 0;
26520     },
26521
26522     /**
26523      * Returns True if the specified row is selected.
26524      * @param {Number/Record} record The record or index of the record to check
26525      * @return {Boolean}
26526      */
26527     isSelected : function(index){
26528             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26529         return (r && this.selections.key(r.id) ? true : false);
26530     },
26531
26532     /**
26533      * Returns True if the specified record id is selected.
26534      * @param {String} id The id of record to check
26535      * @return {Boolean}
26536      */
26537     isIdSelected : function(id){
26538         return (this.selections.key(id) ? true : false);
26539     },
26540
26541
26542     // private
26543     handleMouseDBClick : function(e, t){
26544         
26545     },
26546     // private
26547     handleMouseDown : function(e, t)
26548     {
26549             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26550         if(this.isLocked() || rowIndex < 0 ){
26551             return;
26552         };
26553         if(e.shiftKey && this.last !== false){
26554             var last = this.last;
26555             this.selectRange(last, rowIndex, e.ctrlKey);
26556             this.last = last; // reset the last
26557             t.focus();
26558     
26559         }else{
26560             var isSelected = this.isSelected(rowIndex);
26561             //Roo.log("select row:" + rowIndex);
26562             if(isSelected){
26563                 this.deselectRow(rowIndex);
26564             } else {
26565                         this.selectRow(rowIndex, true);
26566             }
26567     
26568             /*
26569                 if(e.button !== 0 && isSelected){
26570                 alert('rowIndex 2: ' + rowIndex);
26571                     view.focusRow(rowIndex);
26572                 }else if(e.ctrlKey && isSelected){
26573                     this.deselectRow(rowIndex);
26574                 }else if(!isSelected){
26575                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26576                     view.focusRow(rowIndex);
26577                 }
26578             */
26579         }
26580         this.fireEvent("afterselectionchange", this);
26581     },
26582     // private
26583     handleDragableRowClick :  function(grid, rowIndex, e) 
26584     {
26585         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26586             this.selectRow(rowIndex, false);
26587             grid.view.focusRow(rowIndex);
26588              this.fireEvent("afterselectionchange", this);
26589         }
26590     },
26591     
26592     /**
26593      * Selects multiple rows.
26594      * @param {Array} rows Array of the indexes of the row to select
26595      * @param {Boolean} keepExisting (optional) True to keep existing selections
26596      */
26597     selectRows : function(rows, keepExisting){
26598         if(!keepExisting){
26599             this.clearSelections();
26600         }
26601         for(var i = 0, len = rows.length; i < len; i++){
26602             this.selectRow(rows[i], true);
26603         }
26604     },
26605
26606     /**
26607      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26608      * @param {Number} startRow The index of the first row in the range
26609      * @param {Number} endRow The index of the last row in the range
26610      * @param {Boolean} keepExisting (optional) True to retain existing selections
26611      */
26612     selectRange : function(startRow, endRow, keepExisting){
26613         if(this.locked) {
26614             return;
26615         }
26616         if(!keepExisting){
26617             this.clearSelections();
26618         }
26619         if(startRow <= endRow){
26620             for(var i = startRow; i <= endRow; i++){
26621                 this.selectRow(i, true);
26622             }
26623         }else{
26624             for(var i = startRow; i >= endRow; i--){
26625                 this.selectRow(i, true);
26626             }
26627         }
26628     },
26629
26630     /**
26631      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26632      * @param {Number} startRow The index of the first row in the range
26633      * @param {Number} endRow The index of the last row in the range
26634      */
26635     deselectRange : function(startRow, endRow, preventViewNotify){
26636         if(this.locked) {
26637             return;
26638         }
26639         for(var i = startRow; i <= endRow; i++){
26640             this.deselectRow(i, preventViewNotify);
26641         }
26642     },
26643
26644     /**
26645      * Selects a row.
26646      * @param {Number} row The index of the row to select
26647      * @param {Boolean} keepExisting (optional) True to keep existing selections
26648      */
26649     selectRow : function(index, keepExisting, preventViewNotify)
26650     {
26651             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26652             return;
26653         }
26654         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26655             if(!keepExisting || this.singleSelect){
26656                 this.clearSelections();
26657             }
26658             
26659             var r = this.grid.store.getAt(index);
26660             //console.log('selectRow - record id :' + r.id);
26661             
26662             this.selections.add(r);
26663             this.last = this.lastActive = index;
26664             if(!preventViewNotify){
26665                 var proxy = new Roo.Element(
26666                                 this.grid.getRowDom(index)
26667                 );
26668                 proxy.addClass('bg-info info');
26669             }
26670             this.fireEvent("rowselect", this, index, r);
26671             this.fireEvent("selectionchange", this);
26672         }
26673     },
26674
26675     /**
26676      * Deselects a row.
26677      * @param {Number} row The index of the row to deselect
26678      */
26679     deselectRow : function(index, preventViewNotify)
26680     {
26681         if(this.locked) {
26682             return;
26683         }
26684         if(this.last == index){
26685             this.last = false;
26686         }
26687         if(this.lastActive == index){
26688             this.lastActive = false;
26689         }
26690         
26691         var r = this.grid.store.getAt(index);
26692         if (!r) {
26693             return;
26694         }
26695         
26696         this.selections.remove(r);
26697         //.console.log('deselectRow - record id :' + r.id);
26698         if(!preventViewNotify){
26699         
26700             var proxy = new Roo.Element(
26701                 this.grid.getRowDom(index)
26702             );
26703             proxy.removeClass('bg-info info');
26704         }
26705         this.fireEvent("rowdeselect", this, index);
26706         this.fireEvent("selectionchange", this);
26707     },
26708
26709     // private
26710     restoreLast : function(){
26711         if(this._last){
26712             this.last = this._last;
26713         }
26714     },
26715
26716     // private
26717     acceptsNav : function(row, col, cm){
26718         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26719     },
26720
26721     // private
26722     onEditorKey : function(field, e){
26723         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26724         if(k == e.TAB){
26725             e.stopEvent();
26726             ed.completeEdit();
26727             if(e.shiftKey){
26728                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26729             }else{
26730                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26731             }
26732         }else if(k == e.ENTER && !e.ctrlKey){
26733             e.stopEvent();
26734             ed.completeEdit();
26735             if(e.shiftKey){
26736                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26737             }else{
26738                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26739             }
26740         }else if(k == e.ESC){
26741             ed.cancelEdit();
26742         }
26743         if(newCell){
26744             g.startEditing(newCell[0], newCell[1]);
26745         }
26746     }
26747 });
26748 /*
26749  * Based on:
26750  * Ext JS Library 1.1.1
26751  * Copyright(c) 2006-2007, Ext JS, LLC.
26752  *
26753  * Originally Released Under LGPL - original licence link has changed is not relivant.
26754  *
26755  * Fork - LGPL
26756  * <script type="text/javascript">
26757  */
26758  
26759 /**
26760  * @class Roo.bootstrap.PagingToolbar
26761  * @extends Roo.bootstrap.NavSimplebar
26762  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26763  * @constructor
26764  * Create a new PagingToolbar
26765  * @param {Object} config The config object
26766  * @param {Roo.data.Store} store
26767  */
26768 Roo.bootstrap.PagingToolbar = function(config)
26769 {
26770     // old args format still supported... - xtype is prefered..
26771         // created from xtype...
26772     
26773     this.ds = config.dataSource;
26774     
26775     if (config.store && !this.ds) {
26776         this.store= Roo.factory(config.store, Roo.data);
26777         this.ds = this.store;
26778         this.ds.xmodule = this.xmodule || false;
26779     }
26780     
26781     this.toolbarItems = [];
26782     if (config.items) {
26783         this.toolbarItems = config.items;
26784     }
26785     
26786     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26787     
26788     this.cursor = 0;
26789     
26790     if (this.ds) { 
26791         this.bind(this.ds);
26792     }
26793     
26794     if (Roo.bootstrap.version == 4) {
26795         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26796     } else {
26797         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26798     }
26799     
26800 };
26801
26802 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26803     /**
26804      * @cfg {Roo.data.Store} dataSource
26805      * The underlying data store providing the paged data
26806      */
26807     /**
26808      * @cfg {String/HTMLElement/Element} container
26809      * container The id or element that will contain the toolbar
26810      */
26811     /**
26812      * @cfg {Boolean} displayInfo
26813      * True to display the displayMsg (defaults to false)
26814      */
26815     /**
26816      * @cfg {Number} pageSize
26817      * The number of records to display per page (defaults to 20)
26818      */
26819     pageSize: 20,
26820     /**
26821      * @cfg {String} displayMsg
26822      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26823      */
26824     displayMsg : 'Displaying {0} - {1} of {2}',
26825     /**
26826      * @cfg {String} emptyMsg
26827      * The message to display when no records are found (defaults to "No data to display")
26828      */
26829     emptyMsg : 'No data to display',
26830     /**
26831      * Customizable piece of the default paging text (defaults to "Page")
26832      * @type String
26833      */
26834     beforePageText : "Page",
26835     /**
26836      * Customizable piece of the default paging text (defaults to "of %0")
26837      * @type String
26838      */
26839     afterPageText : "of {0}",
26840     /**
26841      * Customizable piece of the default paging text (defaults to "First Page")
26842      * @type String
26843      */
26844     firstText : "First Page",
26845     /**
26846      * Customizable piece of the default paging text (defaults to "Previous Page")
26847      * @type String
26848      */
26849     prevText : "Previous Page",
26850     /**
26851      * Customizable piece of the default paging text (defaults to "Next Page")
26852      * @type String
26853      */
26854     nextText : "Next Page",
26855     /**
26856      * Customizable piece of the default paging text (defaults to "Last Page")
26857      * @type String
26858      */
26859     lastText : "Last Page",
26860     /**
26861      * Customizable piece of the default paging text (defaults to "Refresh")
26862      * @type String
26863      */
26864     refreshText : "Refresh",
26865
26866     buttons : false,
26867     // private
26868     onRender : function(ct, position) 
26869     {
26870         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26871         this.navgroup.parentId = this.id;
26872         this.navgroup.onRender(this.el, null);
26873         // add the buttons to the navgroup
26874         
26875         if(this.displayInfo){
26876             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26877             this.displayEl = this.el.select('.x-paging-info', true).first();
26878 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26879 //            this.displayEl = navel.el.select('span',true).first();
26880         }
26881         
26882         var _this = this;
26883         
26884         if(this.buttons){
26885             Roo.each(_this.buttons, function(e){ // this might need to use render????
26886                Roo.factory(e).render(_this.el);
26887             });
26888         }
26889             
26890         Roo.each(_this.toolbarItems, function(e) {
26891             _this.navgroup.addItem(e);
26892         });
26893         
26894         
26895         this.first = this.navgroup.addItem({
26896             tooltip: this.firstText,
26897             cls: "prev btn-outline-secondary",
26898             html : ' <i class="fa fa-step-backward"></i>',
26899             disabled: true,
26900             preventDefault: true,
26901             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26902         });
26903         
26904         this.prev =  this.navgroup.addItem({
26905             tooltip: this.prevText,
26906             cls: "prev btn-outline-secondary",
26907             html : ' <i class="fa fa-backward"></i>',
26908             disabled: true,
26909             preventDefault: true,
26910             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26911         });
26912     //this.addSeparator();
26913         
26914         
26915         var field = this.navgroup.addItem( {
26916             tagtype : 'span',
26917             cls : 'x-paging-position  btn-outline-secondary',
26918              disabled: true,
26919             html : this.beforePageText  +
26920                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26921                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26922          } ); //?? escaped?
26923         
26924         this.field = field.el.select('input', true).first();
26925         this.field.on("keydown", this.onPagingKeydown, this);
26926         this.field.on("focus", function(){this.dom.select();});
26927     
26928     
26929         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26930         //this.field.setHeight(18);
26931         //this.addSeparator();
26932         this.next = this.navgroup.addItem({
26933             tooltip: this.nextText,
26934             cls: "next btn-outline-secondary",
26935             html : ' <i class="fa fa-forward"></i>',
26936             disabled: true,
26937             preventDefault: true,
26938             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26939         });
26940         this.last = this.navgroup.addItem({
26941             tooltip: this.lastText,
26942             html : ' <i class="fa fa-step-forward"></i>',
26943             cls: "next btn-outline-secondary",
26944             disabled: true,
26945             preventDefault: true,
26946             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26947         });
26948     //this.addSeparator();
26949         this.loading = this.navgroup.addItem({
26950             tooltip: this.refreshText,
26951             cls: "btn-outline-secondary",
26952             html : ' <i class="fa fa-refresh"></i>',
26953             preventDefault: true,
26954             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26955         });
26956         
26957     },
26958
26959     // private
26960     updateInfo : function(){
26961         if(this.displayEl){
26962             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26963             var msg = count == 0 ?
26964                 this.emptyMsg :
26965                 String.format(
26966                     this.displayMsg,
26967                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26968                 );
26969             this.displayEl.update(msg);
26970         }
26971     },
26972
26973     // private
26974     onLoad : function(ds, r, o)
26975     {
26976         this.cursor = o.params.start ? o.params.start : 0;
26977         
26978         var d = this.getPageData(),
26979             ap = d.activePage,
26980             ps = d.pages;
26981         
26982         
26983         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26984         this.field.dom.value = ap;
26985         this.first.setDisabled(ap == 1);
26986         this.prev.setDisabled(ap == 1);
26987         this.next.setDisabled(ap == ps);
26988         this.last.setDisabled(ap == ps);
26989         this.loading.enable();
26990         this.updateInfo();
26991     },
26992
26993     // private
26994     getPageData : function(){
26995         var total = this.ds.getTotalCount();
26996         return {
26997             total : total,
26998             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26999             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27000         };
27001     },
27002
27003     // private
27004     onLoadError : function(){
27005         this.loading.enable();
27006     },
27007
27008     // private
27009     onPagingKeydown : function(e){
27010         var k = e.getKey();
27011         var d = this.getPageData();
27012         if(k == e.RETURN){
27013             var v = this.field.dom.value, pageNum;
27014             if(!v || isNaN(pageNum = parseInt(v, 10))){
27015                 this.field.dom.value = d.activePage;
27016                 return;
27017             }
27018             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27019             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27020             e.stopEvent();
27021         }
27022         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))
27023         {
27024           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27025           this.field.dom.value = pageNum;
27026           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27027           e.stopEvent();
27028         }
27029         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27030         {
27031           var v = this.field.dom.value, pageNum; 
27032           var increment = (e.shiftKey) ? 10 : 1;
27033           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27034                 increment *= -1;
27035           }
27036           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27037             this.field.dom.value = d.activePage;
27038             return;
27039           }
27040           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27041           {
27042             this.field.dom.value = parseInt(v, 10) + increment;
27043             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27044             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27045           }
27046           e.stopEvent();
27047         }
27048     },
27049
27050     // private
27051     beforeLoad : function(){
27052         if(this.loading){
27053             this.loading.disable();
27054         }
27055     },
27056
27057     // private
27058     onClick : function(which){
27059         
27060         var ds = this.ds;
27061         if (!ds) {
27062             return;
27063         }
27064         
27065         switch(which){
27066             case "first":
27067                 ds.load({params:{start: 0, limit: this.pageSize}});
27068             break;
27069             case "prev":
27070                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27071             break;
27072             case "next":
27073                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27074             break;
27075             case "last":
27076                 var total = ds.getTotalCount();
27077                 var extra = total % this.pageSize;
27078                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27079                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27080             break;
27081             case "refresh":
27082                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27083             break;
27084         }
27085     },
27086
27087     /**
27088      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27089      * @param {Roo.data.Store} store The data store to unbind
27090      */
27091     unbind : function(ds){
27092         ds.un("beforeload", this.beforeLoad, this);
27093         ds.un("load", this.onLoad, this);
27094         ds.un("loadexception", this.onLoadError, this);
27095         ds.un("remove", this.updateInfo, this);
27096         ds.un("add", this.updateInfo, this);
27097         this.ds = undefined;
27098     },
27099
27100     /**
27101      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27102      * @param {Roo.data.Store} store The data store to bind
27103      */
27104     bind : function(ds){
27105         ds.on("beforeload", this.beforeLoad, this);
27106         ds.on("load", this.onLoad, this);
27107         ds.on("loadexception", this.onLoadError, this);
27108         ds.on("remove", this.updateInfo, this);
27109         ds.on("add", this.updateInfo, this);
27110         this.ds = ds;
27111     }
27112 });/*
27113  * - LGPL
27114  *
27115  * element
27116  * 
27117  */
27118
27119 /**
27120  * @class Roo.bootstrap.MessageBar
27121  * @extends Roo.bootstrap.Component
27122  * Bootstrap MessageBar class
27123  * @cfg {String} html contents of the MessageBar
27124  * @cfg {String} weight (info | success | warning | danger) default info
27125  * @cfg {String} beforeClass insert the bar before the given class
27126  * @cfg {Boolean} closable (true | false) default false
27127  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27128  * 
27129  * @constructor
27130  * Create a new Element
27131  * @param {Object} config The config object
27132  */
27133
27134 Roo.bootstrap.MessageBar = function(config){
27135     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27136 };
27137
27138 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27139     
27140     html: '',
27141     weight: 'info',
27142     closable: false,
27143     fixed: false,
27144     beforeClass: 'bootstrap-sticky-wrap',
27145     
27146     getAutoCreate : function(){
27147         
27148         var cfg = {
27149             tag: 'div',
27150             cls: 'alert alert-dismissable alert-' + this.weight,
27151             cn: [
27152                 {
27153                     tag: 'span',
27154                     cls: 'message',
27155                     html: this.html || ''
27156                 }
27157             ]
27158         };
27159         
27160         if(this.fixed){
27161             cfg.cls += ' alert-messages-fixed';
27162         }
27163         
27164         if(this.closable){
27165             cfg.cn.push({
27166                 tag: 'button',
27167                 cls: 'close',
27168                 html: 'x'
27169             });
27170         }
27171         
27172         return cfg;
27173     },
27174     
27175     onRender : function(ct, position)
27176     {
27177         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27178         
27179         if(!this.el){
27180             var cfg = Roo.apply({},  this.getAutoCreate());
27181             cfg.id = Roo.id();
27182             
27183             if (this.cls) {
27184                 cfg.cls += ' ' + this.cls;
27185             }
27186             if (this.style) {
27187                 cfg.style = this.style;
27188             }
27189             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27190             
27191             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27192         }
27193         
27194         this.el.select('>button.close').on('click', this.hide, this);
27195         
27196     },
27197     
27198     show : function()
27199     {
27200         if (!this.rendered) {
27201             this.render();
27202         }
27203         
27204         this.el.show();
27205         
27206         this.fireEvent('show', this);
27207         
27208     },
27209     
27210     hide : function()
27211     {
27212         if (!this.rendered) {
27213             this.render();
27214         }
27215         
27216         this.el.hide();
27217         
27218         this.fireEvent('hide', this);
27219     },
27220     
27221     update : function()
27222     {
27223 //        var e = this.el.dom.firstChild;
27224 //        
27225 //        if(this.closable){
27226 //            e = e.nextSibling;
27227 //        }
27228 //        
27229 //        e.data = this.html || '';
27230
27231         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27232     }
27233    
27234 });
27235
27236  
27237
27238      /*
27239  * - LGPL
27240  *
27241  * Graph
27242  * 
27243  */
27244
27245
27246 /**
27247  * @class Roo.bootstrap.Graph
27248  * @extends Roo.bootstrap.Component
27249  * Bootstrap Graph class
27250 > Prameters
27251  -sm {number} sm 4
27252  -md {number} md 5
27253  @cfg {String} graphtype  bar | vbar | pie
27254  @cfg {number} g_x coodinator | centre x (pie)
27255  @cfg {number} g_y coodinator | centre y (pie)
27256  @cfg {number} g_r radius (pie)
27257  @cfg {number} g_height height of the chart (respected by all elements in the set)
27258  @cfg {number} g_width width of the chart (respected by all elements in the set)
27259  @cfg {Object} title The title of the chart
27260     
27261  -{Array}  values
27262  -opts (object) options for the chart 
27263      o {
27264      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27265      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27266      o vgutter (number)
27267      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.
27268      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27269      o to
27270      o stretch (boolean)
27271      o }
27272  -opts (object) options for the pie
27273      o{
27274      o cut
27275      o startAngle (number)
27276      o endAngle (number)
27277      } 
27278  *
27279  * @constructor
27280  * Create a new Input
27281  * @param {Object} config The config object
27282  */
27283
27284 Roo.bootstrap.Graph = function(config){
27285     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27286     
27287     this.addEvents({
27288         // img events
27289         /**
27290          * @event click
27291          * The img click event for the img.
27292          * @param {Roo.EventObject} e
27293          */
27294         "click" : true
27295     });
27296 };
27297
27298 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27299     
27300     sm: 4,
27301     md: 5,
27302     graphtype: 'bar',
27303     g_height: 250,
27304     g_width: 400,
27305     g_x: 50,
27306     g_y: 50,
27307     g_r: 30,
27308     opts:{
27309         //g_colors: this.colors,
27310         g_type: 'soft',
27311         g_gutter: '20%'
27312
27313     },
27314     title : false,
27315
27316     getAutoCreate : function(){
27317         
27318         var cfg = {
27319             tag: 'div',
27320             html : null
27321         };
27322         
27323         
27324         return  cfg;
27325     },
27326
27327     onRender : function(ct,position){
27328         
27329         
27330         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27331         
27332         if (typeof(Raphael) == 'undefined') {
27333             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27334             return;
27335         }
27336         
27337         this.raphael = Raphael(this.el.dom);
27338         
27339                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27340                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27341                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27342                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27343                 /*
27344                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27345                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27346                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27347                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27348                 
27349                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27350                 r.barchart(330, 10, 300, 220, data1);
27351                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27352                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27353                 */
27354                 
27355                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27356                 // r.barchart(30, 30, 560, 250,  xdata, {
27357                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27358                 //     axis : "0 0 1 1",
27359                 //     axisxlabels :  xdata
27360                 //     //yvalues : cols,
27361                    
27362                 // });
27363 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27364 //        
27365 //        this.load(null,xdata,{
27366 //                axis : "0 0 1 1",
27367 //                axisxlabels :  xdata
27368 //                });
27369
27370     },
27371
27372     load : function(graphtype,xdata,opts)
27373     {
27374         this.raphael.clear();
27375         if(!graphtype) {
27376             graphtype = this.graphtype;
27377         }
27378         if(!opts){
27379             opts = this.opts;
27380         }
27381         var r = this.raphael,
27382             fin = function () {
27383                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27384             },
27385             fout = function () {
27386                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27387             },
27388             pfin = function() {
27389                 this.sector.stop();
27390                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27391
27392                 if (this.label) {
27393                     this.label[0].stop();
27394                     this.label[0].attr({ r: 7.5 });
27395                     this.label[1].attr({ "font-weight": 800 });
27396                 }
27397             },
27398             pfout = function() {
27399                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27400
27401                 if (this.label) {
27402                     this.label[0].animate({ r: 5 }, 500, "bounce");
27403                     this.label[1].attr({ "font-weight": 400 });
27404                 }
27405             };
27406
27407         switch(graphtype){
27408             case 'bar':
27409                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27410                 break;
27411             case 'hbar':
27412                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27413                 break;
27414             case 'pie':
27415 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27416 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27417 //            
27418                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27419                 
27420                 break;
27421
27422         }
27423         
27424         if(this.title){
27425             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27426         }
27427         
27428     },
27429     
27430     setTitle: function(o)
27431     {
27432         this.title = o;
27433     },
27434     
27435     initEvents: function() {
27436         
27437         if(!this.href){
27438             this.el.on('click', this.onClick, this);
27439         }
27440     },
27441     
27442     onClick : function(e)
27443     {
27444         Roo.log('img onclick');
27445         this.fireEvent('click', this, e);
27446     }
27447    
27448 });
27449
27450  
27451 /*
27452  * - LGPL
27453  *
27454  * numberBox
27455  * 
27456  */
27457 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27458
27459 /**
27460  * @class Roo.bootstrap.dash.NumberBox
27461  * @extends Roo.bootstrap.Component
27462  * Bootstrap NumberBox class
27463  * @cfg {String} headline Box headline
27464  * @cfg {String} content Box content
27465  * @cfg {String} icon Box icon
27466  * @cfg {String} footer Footer text
27467  * @cfg {String} fhref Footer href
27468  * 
27469  * @constructor
27470  * Create a new NumberBox
27471  * @param {Object} config The config object
27472  */
27473
27474
27475 Roo.bootstrap.dash.NumberBox = function(config){
27476     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27477     
27478 };
27479
27480 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27481     
27482     headline : '',
27483     content : '',
27484     icon : '',
27485     footer : '',
27486     fhref : '',
27487     ficon : '',
27488     
27489     getAutoCreate : function(){
27490         
27491         var cfg = {
27492             tag : 'div',
27493             cls : 'small-box ',
27494             cn : [
27495                 {
27496                     tag : 'div',
27497                     cls : 'inner',
27498                     cn :[
27499                         {
27500                             tag : 'h3',
27501                             cls : 'roo-headline',
27502                             html : this.headline
27503                         },
27504                         {
27505                             tag : 'p',
27506                             cls : 'roo-content',
27507                             html : this.content
27508                         }
27509                     ]
27510                 }
27511             ]
27512         };
27513         
27514         if(this.icon){
27515             cfg.cn.push({
27516                 tag : 'div',
27517                 cls : 'icon',
27518                 cn :[
27519                     {
27520                         tag : 'i',
27521                         cls : 'ion ' + this.icon
27522                     }
27523                 ]
27524             });
27525         }
27526         
27527         if(this.footer){
27528             var footer = {
27529                 tag : 'a',
27530                 cls : 'small-box-footer',
27531                 href : this.fhref || '#',
27532                 html : this.footer
27533             };
27534             
27535             cfg.cn.push(footer);
27536             
27537         }
27538         
27539         return  cfg;
27540     },
27541
27542     onRender : function(ct,position){
27543         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27544
27545
27546        
27547                 
27548     },
27549
27550     setHeadline: function (value)
27551     {
27552         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27553     },
27554     
27555     setFooter: function (value, href)
27556     {
27557         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27558         
27559         if(href){
27560             this.el.select('a.small-box-footer',true).first().attr('href', href);
27561         }
27562         
27563     },
27564
27565     setContent: function (value)
27566     {
27567         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27568     },
27569
27570     initEvents: function() 
27571     {   
27572         
27573     }
27574     
27575 });
27576
27577  
27578 /*
27579  * - LGPL
27580  *
27581  * TabBox
27582  * 
27583  */
27584 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27585
27586 /**
27587  * @class Roo.bootstrap.dash.TabBox
27588  * @extends Roo.bootstrap.Component
27589  * Bootstrap TabBox class
27590  * @cfg {String} title Title of the TabBox
27591  * @cfg {String} icon Icon of the TabBox
27592  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27593  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27594  * 
27595  * @constructor
27596  * Create a new TabBox
27597  * @param {Object} config The config object
27598  */
27599
27600
27601 Roo.bootstrap.dash.TabBox = function(config){
27602     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27603     this.addEvents({
27604         // raw events
27605         /**
27606          * @event addpane
27607          * When a pane is added
27608          * @param {Roo.bootstrap.dash.TabPane} pane
27609          */
27610         "addpane" : true,
27611         /**
27612          * @event activatepane
27613          * When a pane is activated
27614          * @param {Roo.bootstrap.dash.TabPane} pane
27615          */
27616         "activatepane" : true
27617         
27618          
27619     });
27620     
27621     this.panes = [];
27622 };
27623
27624 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27625
27626     title : '',
27627     icon : false,
27628     showtabs : true,
27629     tabScrollable : false,
27630     
27631     getChildContainer : function()
27632     {
27633         return this.el.select('.tab-content', true).first();
27634     },
27635     
27636     getAutoCreate : function(){
27637         
27638         var header = {
27639             tag: 'li',
27640             cls: 'pull-left header',
27641             html: this.title,
27642             cn : []
27643         };
27644         
27645         if(this.icon){
27646             header.cn.push({
27647                 tag: 'i',
27648                 cls: 'fa ' + this.icon
27649             });
27650         }
27651         
27652         var h = {
27653             tag: 'ul',
27654             cls: 'nav nav-tabs pull-right',
27655             cn: [
27656                 header
27657             ]
27658         };
27659         
27660         if(this.tabScrollable){
27661             h = {
27662                 tag: 'div',
27663                 cls: 'tab-header',
27664                 cn: [
27665                     {
27666                         tag: 'ul',
27667                         cls: 'nav nav-tabs pull-right',
27668                         cn: [
27669                             header
27670                         ]
27671                     }
27672                 ]
27673             };
27674         }
27675         
27676         var cfg = {
27677             tag: 'div',
27678             cls: 'nav-tabs-custom',
27679             cn: [
27680                 h,
27681                 {
27682                     tag: 'div',
27683                     cls: 'tab-content no-padding',
27684                     cn: []
27685                 }
27686             ]
27687         };
27688
27689         return  cfg;
27690     },
27691     initEvents : function()
27692     {
27693         //Roo.log('add add pane handler');
27694         this.on('addpane', this.onAddPane, this);
27695     },
27696      /**
27697      * Updates the box title
27698      * @param {String} html to set the title to.
27699      */
27700     setTitle : function(value)
27701     {
27702         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27703     },
27704     onAddPane : function(pane)
27705     {
27706         this.panes.push(pane);
27707         //Roo.log('addpane');
27708         //Roo.log(pane);
27709         // tabs are rendere left to right..
27710         if(!this.showtabs){
27711             return;
27712         }
27713         
27714         var ctr = this.el.select('.nav-tabs', true).first();
27715          
27716          
27717         var existing = ctr.select('.nav-tab',true);
27718         var qty = existing.getCount();;
27719         
27720         
27721         var tab = ctr.createChild({
27722             tag : 'li',
27723             cls : 'nav-tab' + (qty ? '' : ' active'),
27724             cn : [
27725                 {
27726                     tag : 'a',
27727                     href:'#',
27728                     html : pane.title
27729                 }
27730             ]
27731         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27732         pane.tab = tab;
27733         
27734         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27735         if (!qty) {
27736             pane.el.addClass('active');
27737         }
27738         
27739                 
27740     },
27741     onTabClick : function(ev,un,ob,pane)
27742     {
27743         //Roo.log('tab - prev default');
27744         ev.preventDefault();
27745         
27746         
27747         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27748         pane.tab.addClass('active');
27749         //Roo.log(pane.title);
27750         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27751         // technically we should have a deactivate event.. but maybe add later.
27752         // and it should not de-activate the selected tab...
27753         this.fireEvent('activatepane', pane);
27754         pane.el.addClass('active');
27755         pane.fireEvent('activate');
27756         
27757         
27758     },
27759     
27760     getActivePane : function()
27761     {
27762         var r = false;
27763         Roo.each(this.panes, function(p) {
27764             if(p.el.hasClass('active')){
27765                 r = p;
27766                 return false;
27767             }
27768             
27769             return;
27770         });
27771         
27772         return r;
27773     }
27774     
27775     
27776 });
27777
27778  
27779 /*
27780  * - LGPL
27781  *
27782  * Tab pane
27783  * 
27784  */
27785 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27786 /**
27787  * @class Roo.bootstrap.TabPane
27788  * @extends Roo.bootstrap.Component
27789  * Bootstrap TabPane class
27790  * @cfg {Boolean} active (false | true) Default false
27791  * @cfg {String} title title of panel
27792
27793  * 
27794  * @constructor
27795  * Create a new TabPane
27796  * @param {Object} config The config object
27797  */
27798
27799 Roo.bootstrap.dash.TabPane = function(config){
27800     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27801     
27802     this.addEvents({
27803         // raw events
27804         /**
27805          * @event activate
27806          * When a pane is activated
27807          * @param {Roo.bootstrap.dash.TabPane} pane
27808          */
27809         "activate" : true
27810          
27811     });
27812 };
27813
27814 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27815     
27816     active : false,
27817     title : '',
27818     
27819     // the tabBox that this is attached to.
27820     tab : false,
27821      
27822     getAutoCreate : function() 
27823     {
27824         var cfg = {
27825             tag: 'div',
27826             cls: 'tab-pane'
27827         };
27828         
27829         if(this.active){
27830             cfg.cls += ' active';
27831         }
27832         
27833         return cfg;
27834     },
27835     initEvents  : function()
27836     {
27837         //Roo.log('trigger add pane handler');
27838         this.parent().fireEvent('addpane', this)
27839     },
27840     
27841      /**
27842      * Updates the tab title 
27843      * @param {String} html to set the title to.
27844      */
27845     setTitle: function(str)
27846     {
27847         if (!this.tab) {
27848             return;
27849         }
27850         this.title = str;
27851         this.tab.select('a', true).first().dom.innerHTML = str;
27852         
27853     }
27854     
27855     
27856     
27857 });
27858
27859  
27860
27861
27862  /*
27863  * - LGPL
27864  *
27865  * menu
27866  * 
27867  */
27868 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27869
27870 /**
27871  * @class Roo.bootstrap.menu.Menu
27872  * @extends Roo.bootstrap.Component
27873  * Bootstrap Menu class - container for Menu
27874  * @cfg {String} html Text of the menu
27875  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27876  * @cfg {String} icon Font awesome icon
27877  * @cfg {String} pos Menu align to (top | bottom) default bottom
27878  * 
27879  * 
27880  * @constructor
27881  * Create a new Menu
27882  * @param {Object} config The config object
27883  */
27884
27885
27886 Roo.bootstrap.menu.Menu = function(config){
27887     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27888     
27889     this.addEvents({
27890         /**
27891          * @event beforeshow
27892          * Fires before this menu is displayed
27893          * @param {Roo.bootstrap.menu.Menu} this
27894          */
27895         beforeshow : true,
27896         /**
27897          * @event beforehide
27898          * Fires before this menu is hidden
27899          * @param {Roo.bootstrap.menu.Menu} this
27900          */
27901         beforehide : true,
27902         /**
27903          * @event show
27904          * Fires after this menu is displayed
27905          * @param {Roo.bootstrap.menu.Menu} this
27906          */
27907         show : true,
27908         /**
27909          * @event hide
27910          * Fires after this menu is hidden
27911          * @param {Roo.bootstrap.menu.Menu} this
27912          */
27913         hide : true,
27914         /**
27915          * @event click
27916          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27917          * @param {Roo.bootstrap.menu.Menu} this
27918          * @param {Roo.EventObject} e
27919          */
27920         click : true
27921     });
27922     
27923 };
27924
27925 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27926     
27927     submenu : false,
27928     html : '',
27929     weight : 'default',
27930     icon : false,
27931     pos : 'bottom',
27932     
27933     
27934     getChildContainer : function() {
27935         if(this.isSubMenu){
27936             return this.el;
27937         }
27938         
27939         return this.el.select('ul.dropdown-menu', true).first();  
27940     },
27941     
27942     getAutoCreate : function()
27943     {
27944         var text = [
27945             {
27946                 tag : 'span',
27947                 cls : 'roo-menu-text',
27948                 html : this.html
27949             }
27950         ];
27951         
27952         if(this.icon){
27953             text.unshift({
27954                 tag : 'i',
27955                 cls : 'fa ' + this.icon
27956             })
27957         }
27958         
27959         
27960         var cfg = {
27961             tag : 'div',
27962             cls : 'btn-group',
27963             cn : [
27964                 {
27965                     tag : 'button',
27966                     cls : 'dropdown-button btn btn-' + this.weight,
27967                     cn : text
27968                 },
27969                 {
27970                     tag : 'button',
27971                     cls : 'dropdown-toggle btn btn-' + this.weight,
27972                     cn : [
27973                         {
27974                             tag : 'span',
27975                             cls : 'caret'
27976                         }
27977                     ]
27978                 },
27979                 {
27980                     tag : 'ul',
27981                     cls : 'dropdown-menu'
27982                 }
27983             ]
27984             
27985         };
27986         
27987         if(this.pos == 'top'){
27988             cfg.cls += ' dropup';
27989         }
27990         
27991         if(this.isSubMenu){
27992             cfg = {
27993                 tag : 'ul',
27994                 cls : 'dropdown-menu'
27995             }
27996         }
27997         
27998         return cfg;
27999     },
28000     
28001     onRender : function(ct, position)
28002     {
28003         this.isSubMenu = ct.hasClass('dropdown-submenu');
28004         
28005         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28006     },
28007     
28008     initEvents : function() 
28009     {
28010         if(this.isSubMenu){
28011             return;
28012         }
28013         
28014         this.hidden = true;
28015         
28016         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28017         this.triggerEl.on('click', this.onTriggerPress, this);
28018         
28019         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28020         this.buttonEl.on('click', this.onClick, this);
28021         
28022     },
28023     
28024     list : function()
28025     {
28026         if(this.isSubMenu){
28027             return this.el;
28028         }
28029         
28030         return this.el.select('ul.dropdown-menu', true).first();
28031     },
28032     
28033     onClick : function(e)
28034     {
28035         this.fireEvent("click", this, e);
28036     },
28037     
28038     onTriggerPress  : function(e)
28039     {   
28040         if (this.isVisible()) {
28041             this.hide();
28042         } else {
28043             this.show();
28044         }
28045     },
28046     
28047     isVisible : function(){
28048         return !this.hidden;
28049     },
28050     
28051     show : function()
28052     {
28053         this.fireEvent("beforeshow", this);
28054         
28055         this.hidden = false;
28056         this.el.addClass('open');
28057         
28058         Roo.get(document).on("mouseup", this.onMouseUp, this);
28059         
28060         this.fireEvent("show", this);
28061         
28062         
28063     },
28064     
28065     hide : function()
28066     {
28067         this.fireEvent("beforehide", this);
28068         
28069         this.hidden = true;
28070         this.el.removeClass('open');
28071         
28072         Roo.get(document).un("mouseup", this.onMouseUp);
28073         
28074         this.fireEvent("hide", this);
28075     },
28076     
28077     onMouseUp : function()
28078     {
28079         this.hide();
28080     }
28081     
28082 });
28083
28084  
28085  /*
28086  * - LGPL
28087  *
28088  * menu item
28089  * 
28090  */
28091 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28092
28093 /**
28094  * @class Roo.bootstrap.menu.Item
28095  * @extends Roo.bootstrap.Component
28096  * Bootstrap MenuItem class
28097  * @cfg {Boolean} submenu (true | false) default false
28098  * @cfg {String} html text of the item
28099  * @cfg {String} href the link
28100  * @cfg {Boolean} disable (true | false) default false
28101  * @cfg {Boolean} preventDefault (true | false) default true
28102  * @cfg {String} icon Font awesome icon
28103  * @cfg {String} pos Submenu align to (left | right) default right 
28104  * 
28105  * 
28106  * @constructor
28107  * Create a new Item
28108  * @param {Object} config The config object
28109  */
28110
28111
28112 Roo.bootstrap.menu.Item = function(config){
28113     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28114     this.addEvents({
28115         /**
28116          * @event mouseover
28117          * Fires when the mouse is hovering over this menu
28118          * @param {Roo.bootstrap.menu.Item} this
28119          * @param {Roo.EventObject} e
28120          */
28121         mouseover : true,
28122         /**
28123          * @event mouseout
28124          * Fires when the mouse exits this menu
28125          * @param {Roo.bootstrap.menu.Item} this
28126          * @param {Roo.EventObject} e
28127          */
28128         mouseout : true,
28129         // raw events
28130         /**
28131          * @event click
28132          * The raw click event for the entire grid.
28133          * @param {Roo.EventObject} e
28134          */
28135         click : true
28136     });
28137 };
28138
28139 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28140     
28141     submenu : false,
28142     href : '',
28143     html : '',
28144     preventDefault: true,
28145     disable : false,
28146     icon : false,
28147     pos : 'right',
28148     
28149     getAutoCreate : function()
28150     {
28151         var text = [
28152             {
28153                 tag : 'span',
28154                 cls : 'roo-menu-item-text',
28155                 html : this.html
28156             }
28157         ];
28158         
28159         if(this.icon){
28160             text.unshift({
28161                 tag : 'i',
28162                 cls : 'fa ' + this.icon
28163             })
28164         }
28165         
28166         var cfg = {
28167             tag : 'li',
28168             cn : [
28169                 {
28170                     tag : 'a',
28171                     href : this.href || '#',
28172                     cn : text
28173                 }
28174             ]
28175         };
28176         
28177         if(this.disable){
28178             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28179         }
28180         
28181         if(this.submenu){
28182             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28183             
28184             if(this.pos == 'left'){
28185                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28186             }
28187         }
28188         
28189         return cfg;
28190     },
28191     
28192     initEvents : function() 
28193     {
28194         this.el.on('mouseover', this.onMouseOver, this);
28195         this.el.on('mouseout', this.onMouseOut, this);
28196         
28197         this.el.select('a', true).first().on('click', this.onClick, this);
28198         
28199     },
28200     
28201     onClick : function(e)
28202     {
28203         if(this.preventDefault){
28204             e.preventDefault();
28205         }
28206         
28207         this.fireEvent("click", this, e);
28208     },
28209     
28210     onMouseOver : function(e)
28211     {
28212         if(this.submenu && this.pos == 'left'){
28213             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28214         }
28215         
28216         this.fireEvent("mouseover", this, e);
28217     },
28218     
28219     onMouseOut : function(e)
28220     {
28221         this.fireEvent("mouseout", this, e);
28222     }
28223 });
28224
28225  
28226
28227  /*
28228  * - LGPL
28229  *
28230  * menu separator
28231  * 
28232  */
28233 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28234
28235 /**
28236  * @class Roo.bootstrap.menu.Separator
28237  * @extends Roo.bootstrap.Component
28238  * Bootstrap Separator class
28239  * 
28240  * @constructor
28241  * Create a new Separator
28242  * @param {Object} config The config object
28243  */
28244
28245
28246 Roo.bootstrap.menu.Separator = function(config){
28247     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28248 };
28249
28250 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28251     
28252     getAutoCreate : function(){
28253         var cfg = {
28254             tag : 'li',
28255             cls: 'divider'
28256         };
28257         
28258         return cfg;
28259     }
28260    
28261 });
28262
28263  
28264
28265  /*
28266  * - LGPL
28267  *
28268  * Tooltip
28269  * 
28270  */
28271
28272 /**
28273  * @class Roo.bootstrap.Tooltip
28274  * Bootstrap Tooltip class
28275  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28276  * to determine which dom element triggers the tooltip.
28277  * 
28278  * It needs to add support for additional attributes like tooltip-position
28279  * 
28280  * @constructor
28281  * Create a new Toolti
28282  * @param {Object} config The config object
28283  */
28284
28285 Roo.bootstrap.Tooltip = function(config){
28286     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28287     
28288     this.alignment = Roo.bootstrap.Tooltip.alignment;
28289     
28290     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28291         this.alignment = config.alignment;
28292     }
28293     
28294 };
28295
28296 Roo.apply(Roo.bootstrap.Tooltip, {
28297     /**
28298      * @function init initialize tooltip monitoring.
28299      * @static
28300      */
28301     currentEl : false,
28302     currentTip : false,
28303     currentRegion : false,
28304     
28305     //  init : delay?
28306     
28307     init : function()
28308     {
28309         Roo.get(document).on('mouseover', this.enter ,this);
28310         Roo.get(document).on('mouseout', this.leave, this);
28311          
28312         
28313         this.currentTip = new Roo.bootstrap.Tooltip();
28314     },
28315     
28316     enter : function(ev)
28317     {
28318         var dom = ev.getTarget();
28319         
28320         //Roo.log(['enter',dom]);
28321         var el = Roo.fly(dom);
28322         if (this.currentEl) {
28323             //Roo.log(dom);
28324             //Roo.log(this.currentEl);
28325             //Roo.log(this.currentEl.contains(dom));
28326             if (this.currentEl == el) {
28327                 return;
28328             }
28329             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28330                 return;
28331             }
28332
28333         }
28334         
28335         if (this.currentTip.el) {
28336             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28337         }    
28338         //Roo.log(ev);
28339         
28340         if(!el || el.dom == document){
28341             return;
28342         }
28343         
28344         var bindEl = el;
28345         
28346         // you can not look for children, as if el is the body.. then everythign is the child..
28347         if (!el.attr('tooltip')) { //
28348             if (!el.select("[tooltip]").elements.length) {
28349                 return;
28350             }
28351             // is the mouse over this child...?
28352             bindEl = el.select("[tooltip]").first();
28353             var xy = ev.getXY();
28354             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28355                 //Roo.log("not in region.");
28356                 return;
28357             }
28358             //Roo.log("child element over..");
28359             
28360         }
28361         this.currentEl = bindEl;
28362         this.currentTip.bind(bindEl);
28363         this.currentRegion = Roo.lib.Region.getRegion(dom);
28364         this.currentTip.enter();
28365         
28366     },
28367     leave : function(ev)
28368     {
28369         var dom = ev.getTarget();
28370         //Roo.log(['leave',dom]);
28371         if (!this.currentEl) {
28372             return;
28373         }
28374         
28375         
28376         if (dom != this.currentEl.dom) {
28377             return;
28378         }
28379         var xy = ev.getXY();
28380         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28381             return;
28382         }
28383         // only activate leave if mouse cursor is outside... bounding box..
28384         
28385         
28386         
28387         
28388         if (this.currentTip) {
28389             this.currentTip.leave();
28390         }
28391         //Roo.log('clear currentEl');
28392         this.currentEl = false;
28393         
28394         
28395     },
28396     alignment : {
28397         'left' : ['r-l', [-2,0], 'right'],
28398         'right' : ['l-r', [2,0], 'left'],
28399         'bottom' : ['t-b', [0,2], 'top'],
28400         'top' : [ 'b-t', [0,-2], 'bottom']
28401     }
28402     
28403 });
28404
28405
28406 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28407     
28408     
28409     bindEl : false,
28410     
28411     delay : null, // can be { show : 300 , hide: 500}
28412     
28413     timeout : null,
28414     
28415     hoverState : null, //???
28416     
28417     placement : 'bottom', 
28418     
28419     alignment : false,
28420     
28421     getAutoCreate : function(){
28422     
28423         var cfg = {
28424            cls : 'tooltip',   
28425            role : 'tooltip',
28426            cn : [
28427                 {
28428                     cls : 'tooltip-arrow arrow'
28429                 },
28430                 {
28431                     cls : 'tooltip-inner'
28432                 }
28433            ]
28434         };
28435         
28436         return cfg;
28437     },
28438     bind : function(el)
28439     {
28440         this.bindEl = el;
28441     },
28442     
28443     initEvents : function()
28444     {
28445         this.arrowEl = this.el.select('.arrow', true).first();
28446         this.innerEl = this.el.select('.tooltip-inner', true).first();
28447     },
28448     
28449     enter : function () {
28450        
28451         if (this.timeout != null) {
28452             clearTimeout(this.timeout);
28453         }
28454         
28455         this.hoverState = 'in';
28456          //Roo.log("enter - show");
28457         if (!this.delay || !this.delay.show) {
28458             this.show();
28459             return;
28460         }
28461         var _t = this;
28462         this.timeout = setTimeout(function () {
28463             if (_t.hoverState == 'in') {
28464                 _t.show();
28465             }
28466         }, this.delay.show);
28467     },
28468     leave : function()
28469     {
28470         clearTimeout(this.timeout);
28471     
28472         this.hoverState = 'out';
28473          if (!this.delay || !this.delay.hide) {
28474             this.hide();
28475             return;
28476         }
28477        
28478         var _t = this;
28479         this.timeout = setTimeout(function () {
28480             //Roo.log("leave - timeout");
28481             
28482             if (_t.hoverState == 'out') {
28483                 _t.hide();
28484                 Roo.bootstrap.Tooltip.currentEl = false;
28485             }
28486         }, delay);
28487     },
28488     
28489     show : function (msg)
28490     {
28491         if (!this.el) {
28492             this.render(document.body);
28493         }
28494         // set content.
28495         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28496         
28497         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28498         
28499         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28500         
28501         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28502                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28503         
28504         var placement = typeof this.placement == 'function' ?
28505             this.placement.call(this, this.el, on_el) :
28506             this.placement;
28507             
28508         var autoToken = /\s?auto?\s?/i;
28509         var autoPlace = autoToken.test(placement);
28510         if (autoPlace) {
28511             placement = placement.replace(autoToken, '') || 'top';
28512         }
28513         
28514         //this.el.detach()
28515         //this.el.setXY([0,0]);
28516         this.el.show();
28517         //this.el.dom.style.display='block';
28518         
28519         //this.el.appendTo(on_el);
28520         
28521         var p = this.getPosition();
28522         var box = this.el.getBox();
28523         
28524         if (autoPlace) {
28525             // fixme..
28526         }
28527         
28528         var align = this.alignment[placement];
28529         
28530         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28531         
28532         if(placement == 'top' || placement == 'bottom'){
28533             if(xy[0] < 0){
28534                 placement = 'right';
28535             }
28536             
28537             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28538                 placement = 'left';
28539             }
28540             
28541             var scroll = Roo.select('body', true).first().getScroll();
28542             
28543             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28544                 placement = 'top';
28545             }
28546             
28547             align = this.alignment[placement];
28548             
28549             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28550             
28551         }
28552         
28553         this.el.alignTo(this.bindEl, align[0],align[1]);
28554         //var arrow = this.el.select('.arrow',true).first();
28555         //arrow.set(align[2], 
28556         
28557         this.el.addClass(placement);
28558         this.el.addClass("bs-tooltip-"+ placement);
28559         
28560         this.el.addClass('in fade show');
28561         
28562         this.hoverState = null;
28563         
28564         if (this.el.hasClass('fade')) {
28565             // fade it?
28566         }
28567         
28568         
28569         
28570         
28571         
28572     },
28573     hide : function()
28574     {
28575          
28576         if (!this.el) {
28577             return;
28578         }
28579         //this.el.setXY([0,0]);
28580         this.el.removeClass(['show', 'in']);
28581         //this.el.hide();
28582         
28583     }
28584     
28585 });
28586  
28587
28588  /*
28589  * - LGPL
28590  *
28591  * Location Picker
28592  * 
28593  */
28594
28595 /**
28596  * @class Roo.bootstrap.LocationPicker
28597  * @extends Roo.bootstrap.Component
28598  * Bootstrap LocationPicker class
28599  * @cfg {Number} latitude Position when init default 0
28600  * @cfg {Number} longitude Position when init default 0
28601  * @cfg {Number} zoom default 15
28602  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28603  * @cfg {Boolean} mapTypeControl default false
28604  * @cfg {Boolean} disableDoubleClickZoom default false
28605  * @cfg {Boolean} scrollwheel default true
28606  * @cfg {Boolean} streetViewControl default false
28607  * @cfg {Number} radius default 0
28608  * @cfg {String} locationName
28609  * @cfg {Boolean} draggable default true
28610  * @cfg {Boolean} enableAutocomplete default false
28611  * @cfg {Boolean} enableReverseGeocode default true
28612  * @cfg {String} markerTitle
28613  * 
28614  * @constructor
28615  * Create a new LocationPicker
28616  * @param {Object} config The config object
28617  */
28618
28619
28620 Roo.bootstrap.LocationPicker = function(config){
28621     
28622     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28623     
28624     this.addEvents({
28625         /**
28626          * @event initial
28627          * Fires when the picker initialized.
28628          * @param {Roo.bootstrap.LocationPicker} this
28629          * @param {Google Location} location
28630          */
28631         initial : true,
28632         /**
28633          * @event positionchanged
28634          * Fires when the picker position changed.
28635          * @param {Roo.bootstrap.LocationPicker} this
28636          * @param {Google Location} location
28637          */
28638         positionchanged : true,
28639         /**
28640          * @event resize
28641          * Fires when the map resize.
28642          * @param {Roo.bootstrap.LocationPicker} this
28643          */
28644         resize : true,
28645         /**
28646          * @event show
28647          * Fires when the map show.
28648          * @param {Roo.bootstrap.LocationPicker} this
28649          */
28650         show : true,
28651         /**
28652          * @event hide
28653          * Fires when the map hide.
28654          * @param {Roo.bootstrap.LocationPicker} this
28655          */
28656         hide : true,
28657         /**
28658          * @event mapClick
28659          * Fires when click the map.
28660          * @param {Roo.bootstrap.LocationPicker} this
28661          * @param {Map event} e
28662          */
28663         mapClick : true,
28664         /**
28665          * @event mapRightClick
28666          * Fires when right click the map.
28667          * @param {Roo.bootstrap.LocationPicker} this
28668          * @param {Map event} e
28669          */
28670         mapRightClick : true,
28671         /**
28672          * @event markerClick
28673          * Fires when click the marker.
28674          * @param {Roo.bootstrap.LocationPicker} this
28675          * @param {Map event} e
28676          */
28677         markerClick : true,
28678         /**
28679          * @event markerRightClick
28680          * Fires when right click the marker.
28681          * @param {Roo.bootstrap.LocationPicker} this
28682          * @param {Map event} e
28683          */
28684         markerRightClick : true,
28685         /**
28686          * @event OverlayViewDraw
28687          * Fires when OverlayView Draw
28688          * @param {Roo.bootstrap.LocationPicker} this
28689          */
28690         OverlayViewDraw : true,
28691         /**
28692          * @event OverlayViewOnAdd
28693          * Fires when OverlayView Draw
28694          * @param {Roo.bootstrap.LocationPicker} this
28695          */
28696         OverlayViewOnAdd : true,
28697         /**
28698          * @event OverlayViewOnRemove
28699          * Fires when OverlayView Draw
28700          * @param {Roo.bootstrap.LocationPicker} this
28701          */
28702         OverlayViewOnRemove : true,
28703         /**
28704          * @event OverlayViewShow
28705          * Fires when OverlayView Draw
28706          * @param {Roo.bootstrap.LocationPicker} this
28707          * @param {Pixel} cpx
28708          */
28709         OverlayViewShow : true,
28710         /**
28711          * @event OverlayViewHide
28712          * Fires when OverlayView Draw
28713          * @param {Roo.bootstrap.LocationPicker} this
28714          */
28715         OverlayViewHide : true,
28716         /**
28717          * @event loadexception
28718          * Fires when load google lib failed.
28719          * @param {Roo.bootstrap.LocationPicker} this
28720          */
28721         loadexception : true
28722     });
28723         
28724 };
28725
28726 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28727     
28728     gMapContext: false,
28729     
28730     latitude: 0,
28731     longitude: 0,
28732     zoom: 15,
28733     mapTypeId: false,
28734     mapTypeControl: false,
28735     disableDoubleClickZoom: false,
28736     scrollwheel: true,
28737     streetViewControl: false,
28738     radius: 0,
28739     locationName: '',
28740     draggable: true,
28741     enableAutocomplete: false,
28742     enableReverseGeocode: true,
28743     markerTitle: '',
28744     
28745     getAutoCreate: function()
28746     {
28747
28748         var cfg = {
28749             tag: 'div',
28750             cls: 'roo-location-picker'
28751         };
28752         
28753         return cfg
28754     },
28755     
28756     initEvents: function(ct, position)
28757     {       
28758         if(!this.el.getWidth() || this.isApplied()){
28759             return;
28760         }
28761         
28762         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28763         
28764         this.initial();
28765     },
28766     
28767     initial: function()
28768     {
28769         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28770             this.fireEvent('loadexception', this);
28771             return;
28772         }
28773         
28774         if(!this.mapTypeId){
28775             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28776         }
28777         
28778         this.gMapContext = this.GMapContext();
28779         
28780         this.initOverlayView();
28781         
28782         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28783         
28784         var _this = this;
28785                 
28786         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28787             _this.setPosition(_this.gMapContext.marker.position);
28788         });
28789         
28790         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28791             _this.fireEvent('mapClick', this, event);
28792             
28793         });
28794
28795         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28796             _this.fireEvent('mapRightClick', this, event);
28797             
28798         });
28799         
28800         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28801             _this.fireEvent('markerClick', this, event);
28802             
28803         });
28804
28805         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28806             _this.fireEvent('markerRightClick', this, event);
28807             
28808         });
28809         
28810         this.setPosition(this.gMapContext.location);
28811         
28812         this.fireEvent('initial', this, this.gMapContext.location);
28813     },
28814     
28815     initOverlayView: function()
28816     {
28817         var _this = this;
28818         
28819         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28820             
28821             draw: function()
28822             {
28823                 _this.fireEvent('OverlayViewDraw', _this);
28824             },
28825             
28826             onAdd: function()
28827             {
28828                 _this.fireEvent('OverlayViewOnAdd', _this);
28829             },
28830             
28831             onRemove: function()
28832             {
28833                 _this.fireEvent('OverlayViewOnRemove', _this);
28834             },
28835             
28836             show: function(cpx)
28837             {
28838                 _this.fireEvent('OverlayViewShow', _this, cpx);
28839             },
28840             
28841             hide: function()
28842             {
28843                 _this.fireEvent('OverlayViewHide', _this);
28844             }
28845             
28846         });
28847     },
28848     
28849     fromLatLngToContainerPixel: function(event)
28850     {
28851         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28852     },
28853     
28854     isApplied: function() 
28855     {
28856         return this.getGmapContext() == false ? false : true;
28857     },
28858     
28859     getGmapContext: function() 
28860     {
28861         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28862     },
28863     
28864     GMapContext: function() 
28865     {
28866         var position = new google.maps.LatLng(this.latitude, this.longitude);
28867         
28868         var _map = new google.maps.Map(this.el.dom, {
28869             center: position,
28870             zoom: this.zoom,
28871             mapTypeId: this.mapTypeId,
28872             mapTypeControl: this.mapTypeControl,
28873             disableDoubleClickZoom: this.disableDoubleClickZoom,
28874             scrollwheel: this.scrollwheel,
28875             streetViewControl: this.streetViewControl,
28876             locationName: this.locationName,
28877             draggable: this.draggable,
28878             enableAutocomplete: this.enableAutocomplete,
28879             enableReverseGeocode: this.enableReverseGeocode
28880         });
28881         
28882         var _marker = new google.maps.Marker({
28883             position: position,
28884             map: _map,
28885             title: this.markerTitle,
28886             draggable: this.draggable
28887         });
28888         
28889         return {
28890             map: _map,
28891             marker: _marker,
28892             circle: null,
28893             location: position,
28894             radius: this.radius,
28895             locationName: this.locationName,
28896             addressComponents: {
28897                 formatted_address: null,
28898                 addressLine1: null,
28899                 addressLine2: null,
28900                 streetName: null,
28901                 streetNumber: null,
28902                 city: null,
28903                 district: null,
28904                 state: null,
28905                 stateOrProvince: null
28906             },
28907             settings: this,
28908             domContainer: this.el.dom,
28909             geodecoder: new google.maps.Geocoder()
28910         };
28911     },
28912     
28913     drawCircle: function(center, radius, options) 
28914     {
28915         if (this.gMapContext.circle != null) {
28916             this.gMapContext.circle.setMap(null);
28917         }
28918         if (radius > 0) {
28919             radius *= 1;
28920             options = Roo.apply({}, options, {
28921                 strokeColor: "#0000FF",
28922                 strokeOpacity: .35,
28923                 strokeWeight: 2,
28924                 fillColor: "#0000FF",
28925                 fillOpacity: .2
28926             });
28927             
28928             options.map = this.gMapContext.map;
28929             options.radius = radius;
28930             options.center = center;
28931             this.gMapContext.circle = new google.maps.Circle(options);
28932             return this.gMapContext.circle;
28933         }
28934         
28935         return null;
28936     },
28937     
28938     setPosition: function(location) 
28939     {
28940         this.gMapContext.location = location;
28941         this.gMapContext.marker.setPosition(location);
28942         this.gMapContext.map.panTo(location);
28943         this.drawCircle(location, this.gMapContext.radius, {});
28944         
28945         var _this = this;
28946         
28947         if (this.gMapContext.settings.enableReverseGeocode) {
28948             this.gMapContext.geodecoder.geocode({
28949                 latLng: this.gMapContext.location
28950             }, function(results, status) {
28951                 
28952                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28953                     _this.gMapContext.locationName = results[0].formatted_address;
28954                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28955                     
28956                     _this.fireEvent('positionchanged', this, location);
28957                 }
28958             });
28959             
28960             return;
28961         }
28962         
28963         this.fireEvent('positionchanged', this, location);
28964     },
28965     
28966     resize: function()
28967     {
28968         google.maps.event.trigger(this.gMapContext.map, "resize");
28969         
28970         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28971         
28972         this.fireEvent('resize', this);
28973     },
28974     
28975     setPositionByLatLng: function(latitude, longitude)
28976     {
28977         this.setPosition(new google.maps.LatLng(latitude, longitude));
28978     },
28979     
28980     getCurrentPosition: function() 
28981     {
28982         return {
28983             latitude: this.gMapContext.location.lat(),
28984             longitude: this.gMapContext.location.lng()
28985         };
28986     },
28987     
28988     getAddressName: function() 
28989     {
28990         return this.gMapContext.locationName;
28991     },
28992     
28993     getAddressComponents: function() 
28994     {
28995         return this.gMapContext.addressComponents;
28996     },
28997     
28998     address_component_from_google_geocode: function(address_components) 
28999     {
29000         var result = {};
29001         
29002         for (var i = 0; i < address_components.length; i++) {
29003             var component = address_components[i];
29004             if (component.types.indexOf("postal_code") >= 0) {
29005                 result.postalCode = component.short_name;
29006             } else if (component.types.indexOf("street_number") >= 0) {
29007                 result.streetNumber = component.short_name;
29008             } else if (component.types.indexOf("route") >= 0) {
29009                 result.streetName = component.short_name;
29010             } else if (component.types.indexOf("neighborhood") >= 0) {
29011                 result.city = component.short_name;
29012             } else if (component.types.indexOf("locality") >= 0) {
29013                 result.city = component.short_name;
29014             } else if (component.types.indexOf("sublocality") >= 0) {
29015                 result.district = component.short_name;
29016             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29017                 result.stateOrProvince = component.short_name;
29018             } else if (component.types.indexOf("country") >= 0) {
29019                 result.country = component.short_name;
29020             }
29021         }
29022         
29023         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29024         result.addressLine2 = "";
29025         return result;
29026     },
29027     
29028     setZoomLevel: function(zoom)
29029     {
29030         this.gMapContext.map.setZoom(zoom);
29031     },
29032     
29033     show: function()
29034     {
29035         if(!this.el){
29036             return;
29037         }
29038         
29039         this.el.show();
29040         
29041         this.resize();
29042         
29043         this.fireEvent('show', this);
29044     },
29045     
29046     hide: function()
29047     {
29048         if(!this.el){
29049             return;
29050         }
29051         
29052         this.el.hide();
29053         
29054         this.fireEvent('hide', this);
29055     }
29056     
29057 });
29058
29059 Roo.apply(Roo.bootstrap.LocationPicker, {
29060     
29061     OverlayView : function(map, options)
29062     {
29063         options = options || {};
29064         
29065         this.setMap(map);
29066     }
29067     
29068     
29069 });/**
29070  * @class Roo.bootstrap.Alert
29071  * @extends Roo.bootstrap.Component
29072  * Bootstrap Alert class - shows an alert area box
29073  * eg
29074  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29075   Enter a valid email address
29076 </div>
29077  * @licence LGPL
29078  * @cfg {String} title The title of alert
29079  * @cfg {String} html The content of alert
29080  * @cfg {String} weight (  success | info | warning | danger )
29081  * @cfg {String} faicon font-awesomeicon
29082  * 
29083  * @constructor
29084  * Create a new alert
29085  * @param {Object} config The config object
29086  */
29087
29088
29089 Roo.bootstrap.Alert = function(config){
29090     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29091     
29092 };
29093
29094 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29095     
29096     title: '',
29097     html: '',
29098     weight: false,
29099     faicon: false,
29100     
29101     getAutoCreate : function()
29102     {
29103         
29104         var cfg = {
29105             tag : 'div',
29106             cls : 'alert',
29107             cn : [
29108                 {
29109                     tag : 'i',
29110                     cls : 'roo-alert-icon'
29111                     
29112                 },
29113                 {
29114                     tag : 'b',
29115                     cls : 'roo-alert-title',
29116                     html : this.title
29117                 },
29118                 {
29119                     tag : 'span',
29120                     cls : 'roo-alert-text',
29121                     html : this.html
29122                 }
29123             ]
29124         };
29125         
29126         if(this.faicon){
29127             cfg.cn[0].cls += ' fa ' + this.faicon;
29128         }
29129         
29130         if(this.weight){
29131             cfg.cls += ' alert-' + this.weight;
29132         }
29133         
29134         return cfg;
29135     },
29136     
29137     initEvents: function() 
29138     {
29139         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29140     },
29141     
29142     setTitle : function(str)
29143     {
29144         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29145     },
29146     
29147     setText : function(str)
29148     {
29149         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29150     },
29151     
29152     setWeight : function(weight)
29153     {
29154         if(this.weight){
29155             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29156         }
29157         
29158         this.weight = weight;
29159         
29160         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29161     },
29162     
29163     setIcon : function(icon)
29164     {
29165         if(this.faicon){
29166             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29167         }
29168         
29169         this.faicon = icon;
29170         
29171         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29172     },
29173     
29174     hide: function() 
29175     {
29176         this.el.hide();   
29177     },
29178     
29179     show: function() 
29180     {  
29181         this.el.show();   
29182     }
29183     
29184 });
29185
29186  
29187 /*
29188 * Licence: LGPL
29189 */
29190
29191 /**
29192  * @class Roo.bootstrap.UploadCropbox
29193  * @extends Roo.bootstrap.Component
29194  * Bootstrap UploadCropbox class
29195  * @cfg {String} emptyText show when image has been loaded
29196  * @cfg {String} rotateNotify show when image too small to rotate
29197  * @cfg {Number} errorTimeout default 3000
29198  * @cfg {Number} minWidth default 300
29199  * @cfg {Number} minHeight default 300
29200  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29201  * @cfg {Boolean} isDocument (true|false) default false
29202  * @cfg {String} url action url
29203  * @cfg {String} paramName default 'imageUpload'
29204  * @cfg {String} method default POST
29205  * @cfg {Boolean} loadMask (true|false) default true
29206  * @cfg {Boolean} loadingText default 'Loading...'
29207  * 
29208  * @constructor
29209  * Create a new UploadCropbox
29210  * @param {Object} config The config object
29211  */
29212
29213 Roo.bootstrap.UploadCropbox = function(config){
29214     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29215     
29216     this.addEvents({
29217         /**
29218          * @event beforeselectfile
29219          * Fire before select file
29220          * @param {Roo.bootstrap.UploadCropbox} this
29221          */
29222         "beforeselectfile" : true,
29223         /**
29224          * @event initial
29225          * Fire after initEvent
29226          * @param {Roo.bootstrap.UploadCropbox} this
29227          */
29228         "initial" : true,
29229         /**
29230          * @event crop
29231          * Fire after initEvent
29232          * @param {Roo.bootstrap.UploadCropbox} this
29233          * @param {String} data
29234          */
29235         "crop" : true,
29236         /**
29237          * @event prepare
29238          * Fire when preparing the file data
29239          * @param {Roo.bootstrap.UploadCropbox} this
29240          * @param {Object} file
29241          */
29242         "prepare" : true,
29243         /**
29244          * @event exception
29245          * Fire when get exception
29246          * @param {Roo.bootstrap.UploadCropbox} this
29247          * @param {XMLHttpRequest} xhr
29248          */
29249         "exception" : true,
29250         /**
29251          * @event beforeloadcanvas
29252          * Fire before load the canvas
29253          * @param {Roo.bootstrap.UploadCropbox} this
29254          * @param {String} src
29255          */
29256         "beforeloadcanvas" : true,
29257         /**
29258          * @event trash
29259          * Fire when trash image
29260          * @param {Roo.bootstrap.UploadCropbox} this
29261          */
29262         "trash" : true,
29263         /**
29264          * @event download
29265          * Fire when download the image
29266          * @param {Roo.bootstrap.UploadCropbox} this
29267          */
29268         "download" : true,
29269         /**
29270          * @event footerbuttonclick
29271          * Fire when footerbuttonclick
29272          * @param {Roo.bootstrap.UploadCropbox} this
29273          * @param {String} type
29274          */
29275         "footerbuttonclick" : true,
29276         /**
29277          * @event resize
29278          * Fire when resize
29279          * @param {Roo.bootstrap.UploadCropbox} this
29280          */
29281         "resize" : true,
29282         /**
29283          * @event rotate
29284          * Fire when rotate the image
29285          * @param {Roo.bootstrap.UploadCropbox} this
29286          * @param {String} pos
29287          */
29288         "rotate" : true,
29289         /**
29290          * @event inspect
29291          * Fire when inspect the file
29292          * @param {Roo.bootstrap.UploadCropbox} this
29293          * @param {Object} file
29294          */
29295         "inspect" : true,
29296         /**
29297          * @event upload
29298          * Fire when xhr upload the file
29299          * @param {Roo.bootstrap.UploadCropbox} this
29300          * @param {Object} data
29301          */
29302         "upload" : true,
29303         /**
29304          * @event arrange
29305          * Fire when arrange the file data
29306          * @param {Roo.bootstrap.UploadCropbox} this
29307          * @param {Object} formData
29308          */
29309         "arrange" : true
29310     });
29311     
29312     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29313 };
29314
29315 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29316     
29317     emptyText : 'Click to upload image',
29318     rotateNotify : 'Image is too small to rotate',
29319     errorTimeout : 3000,
29320     scale : 0,
29321     baseScale : 1,
29322     rotate : 0,
29323     dragable : false,
29324     pinching : false,
29325     mouseX : 0,
29326     mouseY : 0,
29327     cropData : false,
29328     minWidth : 300,
29329     minHeight : 300,
29330     file : false,
29331     exif : {},
29332     baseRotate : 1,
29333     cropType : 'image/jpeg',
29334     buttons : false,
29335     canvasLoaded : false,
29336     isDocument : false,
29337     method : 'POST',
29338     paramName : 'imageUpload',
29339     loadMask : true,
29340     loadingText : 'Loading...',
29341     maskEl : false,
29342     
29343     getAutoCreate : function()
29344     {
29345         var cfg = {
29346             tag : 'div',
29347             cls : 'roo-upload-cropbox',
29348             cn : [
29349                 {
29350                     tag : 'input',
29351                     cls : 'roo-upload-cropbox-selector',
29352                     type : 'file'
29353                 },
29354                 {
29355                     tag : 'div',
29356                     cls : 'roo-upload-cropbox-body',
29357                     style : 'cursor:pointer',
29358                     cn : [
29359                         {
29360                             tag : 'div',
29361                             cls : 'roo-upload-cropbox-preview'
29362                         },
29363                         {
29364                             tag : 'div',
29365                             cls : 'roo-upload-cropbox-thumb'
29366                         },
29367                         {
29368                             tag : 'div',
29369                             cls : 'roo-upload-cropbox-empty-notify',
29370                             html : this.emptyText
29371                         },
29372                         {
29373                             tag : 'div',
29374                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29375                             html : this.rotateNotify
29376                         }
29377                     ]
29378                 },
29379                 {
29380                     tag : 'div',
29381                     cls : 'roo-upload-cropbox-footer',
29382                     cn : {
29383                         tag : 'div',
29384                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29385                         cn : []
29386                     }
29387                 }
29388             ]
29389         };
29390         
29391         return cfg;
29392     },
29393     
29394     onRender : function(ct, position)
29395     {
29396         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29397         
29398         if (this.buttons.length) {
29399             
29400             Roo.each(this.buttons, function(bb) {
29401                 
29402                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29403                 
29404                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29405                 
29406             }, this);
29407         }
29408         
29409         if(this.loadMask){
29410             this.maskEl = this.el;
29411         }
29412     },
29413     
29414     initEvents : function()
29415     {
29416         this.urlAPI = (window.createObjectURL && window) || 
29417                                 (window.URL && URL.revokeObjectURL && URL) || 
29418                                 (window.webkitURL && webkitURL);
29419                         
29420         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29421         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29422         
29423         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29424         this.selectorEl.hide();
29425         
29426         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29427         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29428         
29429         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29430         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29431         this.thumbEl.hide();
29432         
29433         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29434         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29435         
29436         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29437         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29438         this.errorEl.hide();
29439         
29440         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29441         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29442         this.footerEl.hide();
29443         
29444         this.setThumbBoxSize();
29445         
29446         this.bind();
29447         
29448         this.resize();
29449         
29450         this.fireEvent('initial', this);
29451     },
29452
29453     bind : function()
29454     {
29455         var _this = this;
29456         
29457         window.addEventListener("resize", function() { _this.resize(); } );
29458         
29459         this.bodyEl.on('click', this.beforeSelectFile, this);
29460         
29461         if(Roo.isTouch){
29462             this.bodyEl.on('touchstart', this.onTouchStart, this);
29463             this.bodyEl.on('touchmove', this.onTouchMove, this);
29464             this.bodyEl.on('touchend', this.onTouchEnd, this);
29465         }
29466         
29467         if(!Roo.isTouch){
29468             this.bodyEl.on('mousedown', this.onMouseDown, this);
29469             this.bodyEl.on('mousemove', this.onMouseMove, this);
29470             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29471             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29472             Roo.get(document).on('mouseup', this.onMouseUp, this);
29473         }
29474         
29475         this.selectorEl.on('change', this.onFileSelected, this);
29476     },
29477     
29478     reset : function()
29479     {    
29480         this.scale = 0;
29481         this.baseScale = 1;
29482         this.rotate = 0;
29483         this.baseRotate = 1;
29484         this.dragable = false;
29485         this.pinching = false;
29486         this.mouseX = 0;
29487         this.mouseY = 0;
29488         this.cropData = false;
29489         this.notifyEl.dom.innerHTML = this.emptyText;
29490         
29491         this.selectorEl.dom.value = '';
29492         
29493     },
29494     
29495     resize : function()
29496     {
29497         if(this.fireEvent('resize', this) != false){
29498             this.setThumbBoxPosition();
29499             this.setCanvasPosition();
29500         }
29501     },
29502     
29503     onFooterButtonClick : function(e, el, o, type)
29504     {
29505         switch (type) {
29506             case 'rotate-left' :
29507                 this.onRotateLeft(e);
29508                 break;
29509             case 'rotate-right' :
29510                 this.onRotateRight(e);
29511                 break;
29512             case 'picture' :
29513                 this.beforeSelectFile(e);
29514                 break;
29515             case 'trash' :
29516                 this.trash(e);
29517                 break;
29518             case 'crop' :
29519                 this.crop(e);
29520                 break;
29521             case 'download' :
29522                 this.download(e);
29523                 break;
29524             default :
29525                 break;
29526         }
29527         
29528         this.fireEvent('footerbuttonclick', this, type);
29529     },
29530     
29531     beforeSelectFile : function(e)
29532     {
29533         e.preventDefault();
29534         
29535         if(this.fireEvent('beforeselectfile', this) != false){
29536             this.selectorEl.dom.click();
29537         }
29538     },
29539     
29540     onFileSelected : function(e)
29541     {
29542         e.preventDefault();
29543         
29544         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29545             return;
29546         }
29547         
29548         var file = this.selectorEl.dom.files[0];
29549         
29550         if(this.fireEvent('inspect', this, file) != false){
29551             this.prepare(file);
29552         }
29553         
29554     },
29555     
29556     trash : function(e)
29557     {
29558         this.fireEvent('trash', this);
29559     },
29560     
29561     download : function(e)
29562     {
29563         this.fireEvent('download', this);
29564     },
29565     
29566     loadCanvas : function(src)
29567     {   
29568         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29569             
29570             this.reset();
29571             
29572             this.imageEl = document.createElement('img');
29573             
29574             var _this = this;
29575             
29576             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29577             
29578             this.imageEl.src = src;
29579         }
29580     },
29581     
29582     onLoadCanvas : function()
29583     {   
29584         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29585         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29586         
29587         this.bodyEl.un('click', this.beforeSelectFile, this);
29588         
29589         this.notifyEl.hide();
29590         this.thumbEl.show();
29591         this.footerEl.show();
29592         
29593         this.baseRotateLevel();
29594         
29595         if(this.isDocument){
29596             this.setThumbBoxSize();
29597         }
29598         
29599         this.setThumbBoxPosition();
29600         
29601         this.baseScaleLevel();
29602         
29603         this.draw();
29604         
29605         this.resize();
29606         
29607         this.canvasLoaded = true;
29608         
29609         if(this.loadMask){
29610             this.maskEl.unmask();
29611         }
29612         
29613     },
29614     
29615     setCanvasPosition : function()
29616     {   
29617         if(!this.canvasEl){
29618             return;
29619         }
29620         
29621         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29622         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29623         
29624         this.previewEl.setLeft(pw);
29625         this.previewEl.setTop(ph);
29626         
29627     },
29628     
29629     onMouseDown : function(e)
29630     {   
29631         e.stopEvent();
29632         
29633         this.dragable = true;
29634         this.pinching = false;
29635         
29636         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29637             this.dragable = false;
29638             return;
29639         }
29640         
29641         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29642         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29643         
29644     },
29645     
29646     onMouseMove : function(e)
29647     {   
29648         e.stopEvent();
29649         
29650         if(!this.canvasLoaded){
29651             return;
29652         }
29653         
29654         if (!this.dragable){
29655             return;
29656         }
29657         
29658         var minX = Math.ceil(this.thumbEl.getLeft(true));
29659         var minY = Math.ceil(this.thumbEl.getTop(true));
29660         
29661         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29662         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29663         
29664         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29665         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29666         
29667         x = x - this.mouseX;
29668         y = y - this.mouseY;
29669         
29670         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29671         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29672         
29673         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29674         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29675         
29676         this.previewEl.setLeft(bgX);
29677         this.previewEl.setTop(bgY);
29678         
29679         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29680         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29681     },
29682     
29683     onMouseUp : function(e)
29684     {   
29685         e.stopEvent();
29686         
29687         this.dragable = false;
29688     },
29689     
29690     onMouseWheel : function(e)
29691     {   
29692         e.stopEvent();
29693         
29694         this.startScale = this.scale;
29695         
29696         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29697         
29698         if(!this.zoomable()){
29699             this.scale = this.startScale;
29700             return;
29701         }
29702         
29703         this.draw();
29704         
29705         return;
29706     },
29707     
29708     zoomable : function()
29709     {
29710         var minScale = this.thumbEl.getWidth() / this.minWidth;
29711         
29712         if(this.minWidth < this.minHeight){
29713             minScale = this.thumbEl.getHeight() / this.minHeight;
29714         }
29715         
29716         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29717         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29718         
29719         if(
29720                 this.isDocument &&
29721                 (this.rotate == 0 || this.rotate == 180) && 
29722                 (
29723                     width > this.imageEl.OriginWidth || 
29724                     height > this.imageEl.OriginHeight ||
29725                     (width < this.minWidth && height < this.minHeight)
29726                 )
29727         ){
29728             return false;
29729         }
29730         
29731         if(
29732                 this.isDocument &&
29733                 (this.rotate == 90 || this.rotate == 270) && 
29734                 (
29735                     width > this.imageEl.OriginWidth || 
29736                     height > this.imageEl.OriginHeight ||
29737                     (width < this.minHeight && height < this.minWidth)
29738                 )
29739         ){
29740             return false;
29741         }
29742         
29743         if(
29744                 !this.isDocument &&
29745                 (this.rotate == 0 || this.rotate == 180) && 
29746                 (
29747                     width < this.minWidth || 
29748                     width > this.imageEl.OriginWidth || 
29749                     height < this.minHeight || 
29750                     height > this.imageEl.OriginHeight
29751                 )
29752         ){
29753             return false;
29754         }
29755         
29756         if(
29757                 !this.isDocument &&
29758                 (this.rotate == 90 || this.rotate == 270) && 
29759                 (
29760                     width < this.minHeight || 
29761                     width > this.imageEl.OriginWidth || 
29762                     height < this.minWidth || 
29763                     height > this.imageEl.OriginHeight
29764                 )
29765         ){
29766             return false;
29767         }
29768         
29769         return true;
29770         
29771     },
29772     
29773     onRotateLeft : function(e)
29774     {   
29775         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29776             
29777             var minScale = this.thumbEl.getWidth() / this.minWidth;
29778             
29779             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29780             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29781             
29782             this.startScale = this.scale;
29783             
29784             while (this.getScaleLevel() < minScale){
29785             
29786                 this.scale = this.scale + 1;
29787                 
29788                 if(!this.zoomable()){
29789                     break;
29790                 }
29791                 
29792                 if(
29793                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29794                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29795                 ){
29796                     continue;
29797                 }
29798                 
29799                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29800
29801                 this.draw();
29802                 
29803                 return;
29804             }
29805             
29806             this.scale = this.startScale;
29807             
29808             this.onRotateFail();
29809             
29810             return false;
29811         }
29812         
29813         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29814
29815         if(this.isDocument){
29816             this.setThumbBoxSize();
29817             this.setThumbBoxPosition();
29818             this.setCanvasPosition();
29819         }
29820         
29821         this.draw();
29822         
29823         this.fireEvent('rotate', this, 'left');
29824         
29825     },
29826     
29827     onRotateRight : function(e)
29828     {
29829         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29830             
29831             var minScale = this.thumbEl.getWidth() / this.minWidth;
29832         
29833             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29834             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29835             
29836             this.startScale = this.scale;
29837             
29838             while (this.getScaleLevel() < minScale){
29839             
29840                 this.scale = this.scale + 1;
29841                 
29842                 if(!this.zoomable()){
29843                     break;
29844                 }
29845                 
29846                 if(
29847                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29848                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29849                 ){
29850                     continue;
29851                 }
29852                 
29853                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29854
29855                 this.draw();
29856                 
29857                 return;
29858             }
29859             
29860             this.scale = this.startScale;
29861             
29862             this.onRotateFail();
29863             
29864             return false;
29865         }
29866         
29867         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29868
29869         if(this.isDocument){
29870             this.setThumbBoxSize();
29871             this.setThumbBoxPosition();
29872             this.setCanvasPosition();
29873         }
29874         
29875         this.draw();
29876         
29877         this.fireEvent('rotate', this, 'right');
29878     },
29879     
29880     onRotateFail : function()
29881     {
29882         this.errorEl.show(true);
29883         
29884         var _this = this;
29885         
29886         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29887     },
29888     
29889     draw : function()
29890     {
29891         this.previewEl.dom.innerHTML = '';
29892         
29893         var canvasEl = document.createElement("canvas");
29894         
29895         var contextEl = canvasEl.getContext("2d");
29896         
29897         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29898         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29899         var center = this.imageEl.OriginWidth / 2;
29900         
29901         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29902             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29903             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29904             center = this.imageEl.OriginHeight / 2;
29905         }
29906         
29907         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29908         
29909         contextEl.translate(center, center);
29910         contextEl.rotate(this.rotate * Math.PI / 180);
29911
29912         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29913         
29914         this.canvasEl = document.createElement("canvas");
29915         
29916         this.contextEl = this.canvasEl.getContext("2d");
29917         
29918         switch (this.rotate) {
29919             case 0 :
29920                 
29921                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29922                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29923                 
29924                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29925                 
29926                 break;
29927             case 90 : 
29928                 
29929                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29930                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29931                 
29932                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29933                     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);
29934                     break;
29935                 }
29936                 
29937                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29938                 
29939                 break;
29940             case 180 :
29941                 
29942                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29943                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29944                 
29945                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29946                     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);
29947                     break;
29948                 }
29949                 
29950                 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);
29951                 
29952                 break;
29953             case 270 :
29954                 
29955                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29956                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29957         
29958                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29959                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29960                     break;
29961                 }
29962                 
29963                 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);
29964                 
29965                 break;
29966             default : 
29967                 break;
29968         }
29969         
29970         this.previewEl.appendChild(this.canvasEl);
29971         
29972         this.setCanvasPosition();
29973     },
29974     
29975     crop : function()
29976     {
29977         if(!this.canvasLoaded){
29978             return;
29979         }
29980         
29981         var imageCanvas = document.createElement("canvas");
29982         
29983         var imageContext = imageCanvas.getContext("2d");
29984         
29985         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29986         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29987         
29988         var center = imageCanvas.width / 2;
29989         
29990         imageContext.translate(center, center);
29991         
29992         imageContext.rotate(this.rotate * Math.PI / 180);
29993         
29994         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29995         
29996         var canvas = document.createElement("canvas");
29997         
29998         var context = canvas.getContext("2d");
29999                 
30000         canvas.width = this.minWidth;
30001         canvas.height = this.minHeight;
30002
30003         switch (this.rotate) {
30004             case 0 :
30005                 
30006                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30007                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30008                 
30009                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30010                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30011                 
30012                 var targetWidth = this.minWidth - 2 * x;
30013                 var targetHeight = this.minHeight - 2 * y;
30014                 
30015                 var scale = 1;
30016                 
30017                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30018                     scale = targetWidth / width;
30019                 }
30020                 
30021                 if(x > 0 && y == 0){
30022                     scale = targetHeight / height;
30023                 }
30024                 
30025                 if(x > 0 && y > 0){
30026                     scale = targetWidth / width;
30027                     
30028                     if(width < height){
30029                         scale = targetHeight / height;
30030                     }
30031                 }
30032                 
30033                 context.scale(scale, scale);
30034                 
30035                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30036                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30037
30038                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30039                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30040
30041                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30042                 
30043                 break;
30044             case 90 : 
30045                 
30046                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30047                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30048                 
30049                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30050                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30051                 
30052                 var targetWidth = this.minWidth - 2 * x;
30053                 var targetHeight = this.minHeight - 2 * y;
30054                 
30055                 var scale = 1;
30056                 
30057                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30058                     scale = targetWidth / width;
30059                 }
30060                 
30061                 if(x > 0 && y == 0){
30062                     scale = targetHeight / height;
30063                 }
30064                 
30065                 if(x > 0 && y > 0){
30066                     scale = targetWidth / width;
30067                     
30068                     if(width < height){
30069                         scale = targetHeight / height;
30070                     }
30071                 }
30072                 
30073                 context.scale(scale, scale);
30074                 
30075                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30076                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30077
30078                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30079                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30080                 
30081                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30082                 
30083                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30084                 
30085                 break;
30086             case 180 :
30087                 
30088                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30089                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30090                 
30091                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30092                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30093                 
30094                 var targetWidth = this.minWidth - 2 * x;
30095                 var targetHeight = this.minHeight - 2 * y;
30096                 
30097                 var scale = 1;
30098                 
30099                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30100                     scale = targetWidth / width;
30101                 }
30102                 
30103                 if(x > 0 && y == 0){
30104                     scale = targetHeight / height;
30105                 }
30106                 
30107                 if(x > 0 && y > 0){
30108                     scale = targetWidth / width;
30109                     
30110                     if(width < height){
30111                         scale = targetHeight / height;
30112                     }
30113                 }
30114                 
30115                 context.scale(scale, scale);
30116                 
30117                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30118                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30119
30120                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30121                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30122
30123                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30124                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30125                 
30126                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30127                 
30128                 break;
30129             case 270 :
30130                 
30131                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30132                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30133                 
30134                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30135                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30136                 
30137                 var targetWidth = this.minWidth - 2 * x;
30138                 var targetHeight = this.minHeight - 2 * y;
30139                 
30140                 var scale = 1;
30141                 
30142                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30143                     scale = targetWidth / width;
30144                 }
30145                 
30146                 if(x > 0 && y == 0){
30147                     scale = targetHeight / height;
30148                 }
30149                 
30150                 if(x > 0 && y > 0){
30151                     scale = targetWidth / width;
30152                     
30153                     if(width < height){
30154                         scale = targetHeight / height;
30155                     }
30156                 }
30157                 
30158                 context.scale(scale, scale);
30159                 
30160                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30161                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30162
30163                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30164                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30165                 
30166                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30167                 
30168                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30169                 
30170                 break;
30171             default : 
30172                 break;
30173         }
30174         
30175         this.cropData = canvas.toDataURL(this.cropType);
30176         
30177         if(this.fireEvent('crop', this, this.cropData) !== false){
30178             this.process(this.file, this.cropData);
30179         }
30180         
30181         return;
30182         
30183     },
30184     
30185     setThumbBoxSize : function()
30186     {
30187         var width, height;
30188         
30189         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30190             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30191             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30192             
30193             this.minWidth = width;
30194             this.minHeight = height;
30195             
30196             if(this.rotate == 90 || this.rotate == 270){
30197                 this.minWidth = height;
30198                 this.minHeight = width;
30199             }
30200         }
30201         
30202         height = 300;
30203         width = Math.ceil(this.minWidth * height / this.minHeight);
30204         
30205         if(this.minWidth > this.minHeight){
30206             width = 300;
30207             height = Math.ceil(this.minHeight * width / this.minWidth);
30208         }
30209         
30210         this.thumbEl.setStyle({
30211             width : width + 'px',
30212             height : height + 'px'
30213         });
30214
30215         return;
30216             
30217     },
30218     
30219     setThumbBoxPosition : function()
30220     {
30221         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30222         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30223         
30224         this.thumbEl.setLeft(x);
30225         this.thumbEl.setTop(y);
30226         
30227     },
30228     
30229     baseRotateLevel : function()
30230     {
30231         this.baseRotate = 1;
30232         
30233         if(
30234                 typeof(this.exif) != 'undefined' &&
30235                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30236                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30237         ){
30238             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30239         }
30240         
30241         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30242         
30243     },
30244     
30245     baseScaleLevel : function()
30246     {
30247         var width, height;
30248         
30249         if(this.isDocument){
30250             
30251             if(this.baseRotate == 6 || this.baseRotate == 8){
30252             
30253                 height = this.thumbEl.getHeight();
30254                 this.baseScale = height / this.imageEl.OriginWidth;
30255
30256                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30257                     width = this.thumbEl.getWidth();
30258                     this.baseScale = width / this.imageEl.OriginHeight;
30259                 }
30260
30261                 return;
30262             }
30263
30264             height = this.thumbEl.getHeight();
30265             this.baseScale = height / this.imageEl.OriginHeight;
30266
30267             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30268                 width = this.thumbEl.getWidth();
30269                 this.baseScale = width / this.imageEl.OriginWidth;
30270             }
30271
30272             return;
30273         }
30274         
30275         if(this.baseRotate == 6 || this.baseRotate == 8){
30276             
30277             width = this.thumbEl.getHeight();
30278             this.baseScale = width / this.imageEl.OriginHeight;
30279             
30280             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30281                 height = this.thumbEl.getWidth();
30282                 this.baseScale = height / this.imageEl.OriginHeight;
30283             }
30284             
30285             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30286                 height = this.thumbEl.getWidth();
30287                 this.baseScale = height / this.imageEl.OriginHeight;
30288                 
30289                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30290                     width = this.thumbEl.getHeight();
30291                     this.baseScale = width / this.imageEl.OriginWidth;
30292                 }
30293             }
30294             
30295             return;
30296         }
30297         
30298         width = this.thumbEl.getWidth();
30299         this.baseScale = width / this.imageEl.OriginWidth;
30300         
30301         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30302             height = this.thumbEl.getHeight();
30303             this.baseScale = height / this.imageEl.OriginHeight;
30304         }
30305         
30306         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30307             
30308             height = this.thumbEl.getHeight();
30309             this.baseScale = height / this.imageEl.OriginHeight;
30310             
30311             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30312                 width = this.thumbEl.getWidth();
30313                 this.baseScale = width / this.imageEl.OriginWidth;
30314             }
30315             
30316         }
30317         
30318         return;
30319     },
30320     
30321     getScaleLevel : function()
30322     {
30323         return this.baseScale * Math.pow(1.1, this.scale);
30324     },
30325     
30326     onTouchStart : function(e)
30327     {
30328         if(!this.canvasLoaded){
30329             this.beforeSelectFile(e);
30330             return;
30331         }
30332         
30333         var touches = e.browserEvent.touches;
30334         
30335         if(!touches){
30336             return;
30337         }
30338         
30339         if(touches.length == 1){
30340             this.onMouseDown(e);
30341             return;
30342         }
30343         
30344         if(touches.length != 2){
30345             return;
30346         }
30347         
30348         var coords = [];
30349         
30350         for(var i = 0, finger; finger = touches[i]; i++){
30351             coords.push(finger.pageX, finger.pageY);
30352         }
30353         
30354         var x = Math.pow(coords[0] - coords[2], 2);
30355         var y = Math.pow(coords[1] - coords[3], 2);
30356         
30357         this.startDistance = Math.sqrt(x + y);
30358         
30359         this.startScale = this.scale;
30360         
30361         this.pinching = true;
30362         this.dragable = false;
30363         
30364     },
30365     
30366     onTouchMove : function(e)
30367     {
30368         if(!this.pinching && !this.dragable){
30369             return;
30370         }
30371         
30372         var touches = e.browserEvent.touches;
30373         
30374         if(!touches){
30375             return;
30376         }
30377         
30378         if(this.dragable){
30379             this.onMouseMove(e);
30380             return;
30381         }
30382         
30383         var coords = [];
30384         
30385         for(var i = 0, finger; finger = touches[i]; i++){
30386             coords.push(finger.pageX, finger.pageY);
30387         }
30388         
30389         var x = Math.pow(coords[0] - coords[2], 2);
30390         var y = Math.pow(coords[1] - coords[3], 2);
30391         
30392         this.endDistance = Math.sqrt(x + y);
30393         
30394         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30395         
30396         if(!this.zoomable()){
30397             this.scale = this.startScale;
30398             return;
30399         }
30400         
30401         this.draw();
30402         
30403     },
30404     
30405     onTouchEnd : function(e)
30406     {
30407         this.pinching = false;
30408         this.dragable = false;
30409         
30410     },
30411     
30412     process : function(file, crop)
30413     {
30414         if(this.loadMask){
30415             this.maskEl.mask(this.loadingText);
30416         }
30417         
30418         this.xhr = new XMLHttpRequest();
30419         
30420         file.xhr = this.xhr;
30421
30422         this.xhr.open(this.method, this.url, true);
30423         
30424         var headers = {
30425             "Accept": "application/json",
30426             "Cache-Control": "no-cache",
30427             "X-Requested-With": "XMLHttpRequest"
30428         };
30429         
30430         for (var headerName in headers) {
30431             var headerValue = headers[headerName];
30432             if (headerValue) {
30433                 this.xhr.setRequestHeader(headerName, headerValue);
30434             }
30435         }
30436         
30437         var _this = this;
30438         
30439         this.xhr.onload = function()
30440         {
30441             _this.xhrOnLoad(_this.xhr);
30442         }
30443         
30444         this.xhr.onerror = function()
30445         {
30446             _this.xhrOnError(_this.xhr);
30447         }
30448         
30449         var formData = new FormData();
30450
30451         formData.append('returnHTML', 'NO');
30452         
30453         if(crop){
30454             formData.append('crop', crop);
30455         }
30456         
30457         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30458             formData.append(this.paramName, file, file.name);
30459         }
30460         
30461         if(typeof(file.filename) != 'undefined'){
30462             formData.append('filename', file.filename);
30463         }
30464         
30465         if(typeof(file.mimetype) != 'undefined'){
30466             formData.append('mimetype', file.mimetype);
30467         }
30468         
30469         if(this.fireEvent('arrange', this, formData) != false){
30470             this.xhr.send(formData);
30471         };
30472     },
30473     
30474     xhrOnLoad : function(xhr)
30475     {
30476         if(this.loadMask){
30477             this.maskEl.unmask();
30478         }
30479         
30480         if (xhr.readyState !== 4) {
30481             this.fireEvent('exception', this, xhr);
30482             return;
30483         }
30484
30485         var response = Roo.decode(xhr.responseText);
30486         
30487         if(!response.success){
30488             this.fireEvent('exception', this, xhr);
30489             return;
30490         }
30491         
30492         var response = Roo.decode(xhr.responseText);
30493         
30494         this.fireEvent('upload', this, response);
30495         
30496     },
30497     
30498     xhrOnError : function()
30499     {
30500         if(this.loadMask){
30501             this.maskEl.unmask();
30502         }
30503         
30504         Roo.log('xhr on error');
30505         
30506         var response = Roo.decode(xhr.responseText);
30507           
30508         Roo.log(response);
30509         
30510     },
30511     
30512     prepare : function(file)
30513     {   
30514         if(this.loadMask){
30515             this.maskEl.mask(this.loadingText);
30516         }
30517         
30518         this.file = false;
30519         this.exif = {};
30520         
30521         if(typeof(file) === 'string'){
30522             this.loadCanvas(file);
30523             return;
30524         }
30525         
30526         if(!file || !this.urlAPI){
30527             return;
30528         }
30529         
30530         this.file = file;
30531         this.cropType = file.type;
30532         
30533         var _this = this;
30534         
30535         if(this.fireEvent('prepare', this, this.file) != false){
30536             
30537             var reader = new FileReader();
30538             
30539             reader.onload = function (e) {
30540                 if (e.target.error) {
30541                     Roo.log(e.target.error);
30542                     return;
30543                 }
30544                 
30545                 var buffer = e.target.result,
30546                     dataView = new DataView(buffer),
30547                     offset = 2,
30548                     maxOffset = dataView.byteLength - 4,
30549                     markerBytes,
30550                     markerLength;
30551                 
30552                 if (dataView.getUint16(0) === 0xffd8) {
30553                     while (offset < maxOffset) {
30554                         markerBytes = dataView.getUint16(offset);
30555                         
30556                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30557                             markerLength = dataView.getUint16(offset + 2) + 2;
30558                             if (offset + markerLength > dataView.byteLength) {
30559                                 Roo.log('Invalid meta data: Invalid segment size.');
30560                                 break;
30561                             }
30562                             
30563                             if(markerBytes == 0xffe1){
30564                                 _this.parseExifData(
30565                                     dataView,
30566                                     offset,
30567                                     markerLength
30568                                 );
30569                             }
30570                             
30571                             offset += markerLength;
30572                             
30573                             continue;
30574                         }
30575                         
30576                         break;
30577                     }
30578                     
30579                 }
30580                 
30581                 var url = _this.urlAPI.createObjectURL(_this.file);
30582                 
30583                 _this.loadCanvas(url);
30584                 
30585                 return;
30586             }
30587             
30588             reader.readAsArrayBuffer(this.file);
30589             
30590         }
30591         
30592     },
30593     
30594     parseExifData : function(dataView, offset, length)
30595     {
30596         var tiffOffset = offset + 10,
30597             littleEndian,
30598             dirOffset;
30599     
30600         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30601             // No Exif data, might be XMP data instead
30602             return;
30603         }
30604         
30605         // Check for the ASCII code for "Exif" (0x45786966):
30606         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30607             // No Exif data, might be XMP data instead
30608             return;
30609         }
30610         if (tiffOffset + 8 > dataView.byteLength) {
30611             Roo.log('Invalid Exif data: Invalid segment size.');
30612             return;
30613         }
30614         // Check for the two null bytes:
30615         if (dataView.getUint16(offset + 8) !== 0x0000) {
30616             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30617             return;
30618         }
30619         // Check the byte alignment:
30620         switch (dataView.getUint16(tiffOffset)) {
30621         case 0x4949:
30622             littleEndian = true;
30623             break;
30624         case 0x4D4D:
30625             littleEndian = false;
30626             break;
30627         default:
30628             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30629             return;
30630         }
30631         // Check for the TIFF tag marker (0x002A):
30632         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30633             Roo.log('Invalid Exif data: Missing TIFF marker.');
30634             return;
30635         }
30636         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30637         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30638         
30639         this.parseExifTags(
30640             dataView,
30641             tiffOffset,
30642             tiffOffset + dirOffset,
30643             littleEndian
30644         );
30645     },
30646     
30647     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30648     {
30649         var tagsNumber,
30650             dirEndOffset,
30651             i;
30652         if (dirOffset + 6 > dataView.byteLength) {
30653             Roo.log('Invalid Exif data: Invalid directory offset.');
30654             return;
30655         }
30656         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30657         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30658         if (dirEndOffset + 4 > dataView.byteLength) {
30659             Roo.log('Invalid Exif data: Invalid directory size.');
30660             return;
30661         }
30662         for (i = 0; i < tagsNumber; i += 1) {
30663             this.parseExifTag(
30664                 dataView,
30665                 tiffOffset,
30666                 dirOffset + 2 + 12 * i, // tag offset
30667                 littleEndian
30668             );
30669         }
30670         // Return the offset to the next directory:
30671         return dataView.getUint32(dirEndOffset, littleEndian);
30672     },
30673     
30674     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30675     {
30676         var tag = dataView.getUint16(offset, littleEndian);
30677         
30678         this.exif[tag] = this.getExifValue(
30679             dataView,
30680             tiffOffset,
30681             offset,
30682             dataView.getUint16(offset + 2, littleEndian), // tag type
30683             dataView.getUint32(offset + 4, littleEndian), // tag length
30684             littleEndian
30685         );
30686     },
30687     
30688     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30689     {
30690         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30691             tagSize,
30692             dataOffset,
30693             values,
30694             i,
30695             str,
30696             c;
30697     
30698         if (!tagType) {
30699             Roo.log('Invalid Exif data: Invalid tag type.');
30700             return;
30701         }
30702         
30703         tagSize = tagType.size * length;
30704         // Determine if the value is contained in the dataOffset bytes,
30705         // or if the value at the dataOffset is a pointer to the actual data:
30706         dataOffset = tagSize > 4 ?
30707                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30708         if (dataOffset + tagSize > dataView.byteLength) {
30709             Roo.log('Invalid Exif data: Invalid data offset.');
30710             return;
30711         }
30712         if (length === 1) {
30713             return tagType.getValue(dataView, dataOffset, littleEndian);
30714         }
30715         values = [];
30716         for (i = 0; i < length; i += 1) {
30717             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30718         }
30719         
30720         if (tagType.ascii) {
30721             str = '';
30722             // Concatenate the chars:
30723             for (i = 0; i < values.length; i += 1) {
30724                 c = values[i];
30725                 // Ignore the terminating NULL byte(s):
30726                 if (c === '\u0000') {
30727                     break;
30728                 }
30729                 str += c;
30730             }
30731             return str;
30732         }
30733         return values;
30734     }
30735     
30736 });
30737
30738 Roo.apply(Roo.bootstrap.UploadCropbox, {
30739     tags : {
30740         'Orientation': 0x0112
30741     },
30742     
30743     Orientation: {
30744             1: 0, //'top-left',
30745 //            2: 'top-right',
30746             3: 180, //'bottom-right',
30747 //            4: 'bottom-left',
30748 //            5: 'left-top',
30749             6: 90, //'right-top',
30750 //            7: 'right-bottom',
30751             8: 270 //'left-bottom'
30752     },
30753     
30754     exifTagTypes : {
30755         // byte, 8-bit unsigned int:
30756         1: {
30757             getValue: function (dataView, dataOffset) {
30758                 return dataView.getUint8(dataOffset);
30759             },
30760             size: 1
30761         },
30762         // ascii, 8-bit byte:
30763         2: {
30764             getValue: function (dataView, dataOffset) {
30765                 return String.fromCharCode(dataView.getUint8(dataOffset));
30766             },
30767             size: 1,
30768             ascii: true
30769         },
30770         // short, 16 bit int:
30771         3: {
30772             getValue: function (dataView, dataOffset, littleEndian) {
30773                 return dataView.getUint16(dataOffset, littleEndian);
30774             },
30775             size: 2
30776         },
30777         // long, 32 bit int:
30778         4: {
30779             getValue: function (dataView, dataOffset, littleEndian) {
30780                 return dataView.getUint32(dataOffset, littleEndian);
30781             },
30782             size: 4
30783         },
30784         // rational = two long values, first is numerator, second is denominator:
30785         5: {
30786             getValue: function (dataView, dataOffset, littleEndian) {
30787                 return dataView.getUint32(dataOffset, littleEndian) /
30788                     dataView.getUint32(dataOffset + 4, littleEndian);
30789             },
30790             size: 8
30791         },
30792         // slong, 32 bit signed int:
30793         9: {
30794             getValue: function (dataView, dataOffset, littleEndian) {
30795                 return dataView.getInt32(dataOffset, littleEndian);
30796             },
30797             size: 4
30798         },
30799         // srational, two slongs, first is numerator, second is denominator:
30800         10: {
30801             getValue: function (dataView, dataOffset, littleEndian) {
30802                 return dataView.getInt32(dataOffset, littleEndian) /
30803                     dataView.getInt32(dataOffset + 4, littleEndian);
30804             },
30805             size: 8
30806         }
30807     },
30808     
30809     footer : {
30810         STANDARD : [
30811             {
30812                 tag : 'div',
30813                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30814                 action : 'rotate-left',
30815                 cn : [
30816                     {
30817                         tag : 'button',
30818                         cls : 'btn btn-default',
30819                         html : '<i class="fa fa-undo"></i>'
30820                     }
30821                 ]
30822             },
30823             {
30824                 tag : 'div',
30825                 cls : 'btn-group roo-upload-cropbox-picture',
30826                 action : 'picture',
30827                 cn : [
30828                     {
30829                         tag : 'button',
30830                         cls : 'btn btn-default',
30831                         html : '<i class="fa fa-picture-o"></i>'
30832                     }
30833                 ]
30834             },
30835             {
30836                 tag : 'div',
30837                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30838                 action : 'rotate-right',
30839                 cn : [
30840                     {
30841                         tag : 'button',
30842                         cls : 'btn btn-default',
30843                         html : '<i class="fa fa-repeat"></i>'
30844                     }
30845                 ]
30846             }
30847         ],
30848         DOCUMENT : [
30849             {
30850                 tag : 'div',
30851                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30852                 action : 'rotate-left',
30853                 cn : [
30854                     {
30855                         tag : 'button',
30856                         cls : 'btn btn-default',
30857                         html : '<i class="fa fa-undo"></i>'
30858                     }
30859                 ]
30860             },
30861             {
30862                 tag : 'div',
30863                 cls : 'btn-group roo-upload-cropbox-download',
30864                 action : 'download',
30865                 cn : [
30866                     {
30867                         tag : 'button',
30868                         cls : 'btn btn-default',
30869                         html : '<i class="fa fa-download"></i>'
30870                     }
30871                 ]
30872             },
30873             {
30874                 tag : 'div',
30875                 cls : 'btn-group roo-upload-cropbox-crop',
30876                 action : 'crop',
30877                 cn : [
30878                     {
30879                         tag : 'button',
30880                         cls : 'btn btn-default',
30881                         html : '<i class="fa fa-crop"></i>'
30882                     }
30883                 ]
30884             },
30885             {
30886                 tag : 'div',
30887                 cls : 'btn-group roo-upload-cropbox-trash',
30888                 action : 'trash',
30889                 cn : [
30890                     {
30891                         tag : 'button',
30892                         cls : 'btn btn-default',
30893                         html : '<i class="fa fa-trash"></i>'
30894                     }
30895                 ]
30896             },
30897             {
30898                 tag : 'div',
30899                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30900                 action : 'rotate-right',
30901                 cn : [
30902                     {
30903                         tag : 'button',
30904                         cls : 'btn btn-default',
30905                         html : '<i class="fa fa-repeat"></i>'
30906                     }
30907                 ]
30908             }
30909         ],
30910         ROTATOR : [
30911             {
30912                 tag : 'div',
30913                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30914                 action : 'rotate-left',
30915                 cn : [
30916                     {
30917                         tag : 'button',
30918                         cls : 'btn btn-default',
30919                         html : '<i class="fa fa-undo"></i>'
30920                     }
30921                 ]
30922             },
30923             {
30924                 tag : 'div',
30925                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30926                 action : 'rotate-right',
30927                 cn : [
30928                     {
30929                         tag : 'button',
30930                         cls : 'btn btn-default',
30931                         html : '<i class="fa fa-repeat"></i>'
30932                     }
30933                 ]
30934             }
30935         ]
30936     }
30937 });
30938
30939 /*
30940 * Licence: LGPL
30941 */
30942
30943 /**
30944  * @class Roo.bootstrap.DocumentManager
30945  * @extends Roo.bootstrap.Component
30946  * Bootstrap DocumentManager class
30947  * @cfg {String} paramName default 'imageUpload'
30948  * @cfg {String} toolTipName default 'filename'
30949  * @cfg {String} method default POST
30950  * @cfg {String} url action url
30951  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30952  * @cfg {Boolean} multiple multiple upload default true
30953  * @cfg {Number} thumbSize default 300
30954  * @cfg {String} fieldLabel
30955  * @cfg {Number} labelWidth default 4
30956  * @cfg {String} labelAlign (left|top) default left
30957  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30958 * @cfg {Number} labellg set the width of label (1-12)
30959  * @cfg {Number} labelmd set the width of label (1-12)
30960  * @cfg {Number} labelsm set the width of label (1-12)
30961  * @cfg {Number} labelxs set the width of label (1-12)
30962  * 
30963  * @constructor
30964  * Create a new DocumentManager
30965  * @param {Object} config The config object
30966  */
30967
30968 Roo.bootstrap.DocumentManager = function(config){
30969     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30970     
30971     this.files = [];
30972     this.delegates = [];
30973     
30974     this.addEvents({
30975         /**
30976          * @event initial
30977          * Fire when initial the DocumentManager
30978          * @param {Roo.bootstrap.DocumentManager} this
30979          */
30980         "initial" : true,
30981         /**
30982          * @event inspect
30983          * inspect selected file
30984          * @param {Roo.bootstrap.DocumentManager} this
30985          * @param {File} file
30986          */
30987         "inspect" : true,
30988         /**
30989          * @event exception
30990          * Fire when xhr load exception
30991          * @param {Roo.bootstrap.DocumentManager} this
30992          * @param {XMLHttpRequest} xhr
30993          */
30994         "exception" : true,
30995         /**
30996          * @event afterupload
30997          * Fire when xhr load exception
30998          * @param {Roo.bootstrap.DocumentManager} this
30999          * @param {XMLHttpRequest} xhr
31000          */
31001         "afterupload" : true,
31002         /**
31003          * @event prepare
31004          * prepare the form data
31005          * @param {Roo.bootstrap.DocumentManager} this
31006          * @param {Object} formData
31007          */
31008         "prepare" : true,
31009         /**
31010          * @event remove
31011          * Fire when remove the file
31012          * @param {Roo.bootstrap.DocumentManager} this
31013          * @param {Object} file
31014          */
31015         "remove" : true,
31016         /**
31017          * @event refresh
31018          * Fire after refresh the file
31019          * @param {Roo.bootstrap.DocumentManager} this
31020          */
31021         "refresh" : true,
31022         /**
31023          * @event click
31024          * Fire after click the image
31025          * @param {Roo.bootstrap.DocumentManager} this
31026          * @param {Object} file
31027          */
31028         "click" : true,
31029         /**
31030          * @event edit
31031          * Fire when upload a image and editable set to true
31032          * @param {Roo.bootstrap.DocumentManager} this
31033          * @param {Object} file
31034          */
31035         "edit" : true,
31036         /**
31037          * @event beforeselectfile
31038          * Fire before select file
31039          * @param {Roo.bootstrap.DocumentManager} this
31040          */
31041         "beforeselectfile" : true,
31042         /**
31043          * @event process
31044          * Fire before process file
31045          * @param {Roo.bootstrap.DocumentManager} this
31046          * @param {Object} file
31047          */
31048         "process" : true,
31049         /**
31050          * @event previewrendered
31051          * Fire when preview rendered
31052          * @param {Roo.bootstrap.DocumentManager} this
31053          * @param {Object} file
31054          */
31055         "previewrendered" : true,
31056         /**
31057          */
31058         "previewResize" : true
31059         
31060     });
31061 };
31062
31063 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31064     
31065     boxes : 0,
31066     inputName : '',
31067     thumbSize : 300,
31068     multiple : true,
31069     files : false,
31070     method : 'POST',
31071     url : '',
31072     paramName : 'imageUpload',
31073     toolTipName : 'filename',
31074     fieldLabel : '',
31075     labelWidth : 4,
31076     labelAlign : 'left',
31077     editable : true,
31078     delegates : false,
31079     xhr : false, 
31080     
31081     labellg : 0,
31082     labelmd : 0,
31083     labelsm : 0,
31084     labelxs : 0,
31085     
31086     getAutoCreate : function()
31087     {   
31088         var managerWidget = {
31089             tag : 'div',
31090             cls : 'roo-document-manager',
31091             cn : [
31092                 {
31093                     tag : 'input',
31094                     cls : 'roo-document-manager-selector',
31095                     type : 'file'
31096                 },
31097                 {
31098                     tag : 'div',
31099                     cls : 'roo-document-manager-uploader',
31100                     cn : [
31101                         {
31102                             tag : 'div',
31103                             cls : 'roo-document-manager-upload-btn',
31104                             html : '<i class="fa fa-plus"></i>'
31105                         }
31106                     ]
31107                     
31108                 }
31109             ]
31110         };
31111         
31112         var content = [
31113             {
31114                 tag : 'div',
31115                 cls : 'column col-md-12',
31116                 cn : managerWidget
31117             }
31118         ];
31119         
31120         if(this.fieldLabel.length){
31121             
31122             content = [
31123                 {
31124                     tag : 'div',
31125                     cls : 'column col-md-12',
31126                     html : this.fieldLabel
31127                 },
31128                 {
31129                     tag : 'div',
31130                     cls : 'column col-md-12',
31131                     cn : managerWidget
31132                 }
31133             ];
31134
31135             if(this.labelAlign == 'left'){
31136                 content = [
31137                     {
31138                         tag : 'div',
31139                         cls : 'column',
31140                         html : this.fieldLabel
31141                     },
31142                     {
31143                         tag : 'div',
31144                         cls : 'column',
31145                         cn : managerWidget
31146                     }
31147                 ];
31148                 
31149                 if(this.labelWidth > 12){
31150                     content[0].style = "width: " + this.labelWidth + 'px';
31151                 }
31152
31153                 if(this.labelWidth < 13 && this.labelmd == 0){
31154                     this.labelmd = this.labelWidth;
31155                 }
31156
31157                 if(this.labellg > 0){
31158                     content[0].cls += ' col-lg-' + this.labellg;
31159                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31160                 }
31161
31162                 if(this.labelmd > 0){
31163                     content[0].cls += ' col-md-' + this.labelmd;
31164                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31165                 }
31166
31167                 if(this.labelsm > 0){
31168                     content[0].cls += ' col-sm-' + this.labelsm;
31169                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31170                 }
31171
31172                 if(this.labelxs > 0){
31173                     content[0].cls += ' col-xs-' + this.labelxs;
31174                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31175                 }
31176                 
31177             }
31178         }
31179         
31180         var cfg = {
31181             tag : 'div',
31182             cls : 'row clearfix',
31183             cn : content
31184         };
31185         
31186         return cfg;
31187         
31188     },
31189     
31190     initEvents : function()
31191     {
31192         this.managerEl = this.el.select('.roo-document-manager', true).first();
31193         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31194         
31195         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31196         this.selectorEl.hide();
31197         
31198         if(this.multiple){
31199             this.selectorEl.attr('multiple', 'multiple');
31200         }
31201         
31202         this.selectorEl.on('change', this.onFileSelected, this);
31203         
31204         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31205         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31206         
31207         this.uploader.on('click', this.onUploaderClick, this);
31208         
31209         this.renderProgressDialog();
31210         
31211         var _this = this;
31212         
31213         window.addEventListener("resize", function() { _this.refresh(); } );
31214         
31215         this.fireEvent('initial', this);
31216     },
31217     
31218     renderProgressDialog : function()
31219     {
31220         var _this = this;
31221         
31222         this.progressDialog = new Roo.bootstrap.Modal({
31223             cls : 'roo-document-manager-progress-dialog',
31224             allow_close : false,
31225             animate : false,
31226             title : '',
31227             buttons : [
31228                 {
31229                     name  :'cancel',
31230                     weight : 'danger',
31231                     html : 'Cancel'
31232                 }
31233             ], 
31234             listeners : { 
31235                 btnclick : function() {
31236                     _this.uploadCancel();
31237                     this.hide();
31238                 }
31239             }
31240         });
31241          
31242         this.progressDialog.render(Roo.get(document.body));
31243          
31244         this.progress = new Roo.bootstrap.Progress({
31245             cls : 'roo-document-manager-progress',
31246             active : true,
31247             striped : true
31248         });
31249         
31250         this.progress.render(this.progressDialog.getChildContainer());
31251         
31252         this.progressBar = new Roo.bootstrap.ProgressBar({
31253             cls : 'roo-document-manager-progress-bar',
31254             aria_valuenow : 0,
31255             aria_valuemin : 0,
31256             aria_valuemax : 12,
31257             panel : 'success'
31258         });
31259         
31260         this.progressBar.render(this.progress.getChildContainer());
31261     },
31262     
31263     onUploaderClick : function(e)
31264     {
31265         e.preventDefault();
31266      
31267         if(this.fireEvent('beforeselectfile', this) != false){
31268             this.selectorEl.dom.click();
31269         }
31270         
31271     },
31272     
31273     onFileSelected : function(e)
31274     {
31275         e.preventDefault();
31276         
31277         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31278             return;
31279         }
31280         
31281         Roo.each(this.selectorEl.dom.files, function(file){
31282             if(this.fireEvent('inspect', this, file) != false){
31283                 this.files.push(file);
31284             }
31285         }, this);
31286         
31287         this.queue();
31288         
31289     },
31290     
31291     queue : function()
31292     {
31293         this.selectorEl.dom.value = '';
31294         
31295         if(!this.files || !this.files.length){
31296             return;
31297         }
31298         
31299         if(this.boxes > 0 && this.files.length > this.boxes){
31300             this.files = this.files.slice(0, this.boxes);
31301         }
31302         
31303         this.uploader.show();
31304         
31305         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31306             this.uploader.hide();
31307         }
31308         
31309         var _this = this;
31310         
31311         var files = [];
31312         
31313         var docs = [];
31314         
31315         Roo.each(this.files, function(file){
31316             
31317             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31318                 var f = this.renderPreview(file);
31319                 files.push(f);
31320                 return;
31321             }
31322             
31323             if(file.type.indexOf('image') != -1){
31324                 this.delegates.push(
31325                     (function(){
31326                         _this.process(file);
31327                     }).createDelegate(this)
31328                 );
31329         
31330                 return;
31331             }
31332             
31333             docs.push(
31334                 (function(){
31335                     _this.process(file);
31336                 }).createDelegate(this)
31337             );
31338             
31339         }, this);
31340         
31341         this.files = files;
31342         
31343         this.delegates = this.delegates.concat(docs);
31344         
31345         if(!this.delegates.length){
31346             this.refresh();
31347             return;
31348         }
31349         
31350         this.progressBar.aria_valuemax = this.delegates.length;
31351         
31352         this.arrange();
31353         
31354         return;
31355     },
31356     
31357     arrange : function()
31358     {
31359         if(!this.delegates.length){
31360             this.progressDialog.hide();
31361             this.refresh();
31362             return;
31363         }
31364         
31365         var delegate = this.delegates.shift();
31366         
31367         this.progressDialog.show();
31368         
31369         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31370         
31371         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31372         
31373         delegate();
31374     },
31375     
31376     refresh : function()
31377     {
31378         this.uploader.show();
31379         
31380         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31381             this.uploader.hide();
31382         }
31383         
31384         Roo.isTouch ? this.closable(false) : this.closable(true);
31385         
31386         this.fireEvent('refresh', this);
31387     },
31388     
31389     onRemove : function(e, el, o)
31390     {
31391         e.preventDefault();
31392         
31393         this.fireEvent('remove', this, o);
31394         
31395     },
31396     
31397     remove : function(o)
31398     {
31399         var files = [];
31400         
31401         Roo.each(this.files, function(file){
31402             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31403                 files.push(file);
31404                 return;
31405             }
31406
31407             o.target.remove();
31408
31409         }, this);
31410         
31411         this.files = files;
31412         
31413         this.refresh();
31414     },
31415     
31416     clear : function()
31417     {
31418         Roo.each(this.files, function(file){
31419             if(!file.target){
31420                 return;
31421             }
31422             
31423             file.target.remove();
31424
31425         }, this);
31426         
31427         this.files = [];
31428         
31429         this.refresh();
31430     },
31431     
31432     onClick : function(e, el, o)
31433     {
31434         e.preventDefault();
31435         
31436         this.fireEvent('click', this, o);
31437         
31438     },
31439     
31440     closable : function(closable)
31441     {
31442         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31443             
31444             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31445             
31446             if(closable){
31447                 el.show();
31448                 return;
31449             }
31450             
31451             el.hide();
31452             
31453         }, this);
31454     },
31455     
31456     xhrOnLoad : function(xhr)
31457     {
31458         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31459             el.remove();
31460         }, this);
31461         
31462         if (xhr.readyState !== 4) {
31463             this.arrange();
31464             this.fireEvent('exception', this, xhr);
31465             return;
31466         }
31467
31468         var response = Roo.decode(xhr.responseText);
31469         
31470         if(!response.success){
31471             this.arrange();
31472             this.fireEvent('exception', this, xhr);
31473             return;
31474         }
31475         
31476         var file = this.renderPreview(response.data);
31477         
31478         this.files.push(file);
31479         
31480         this.arrange();
31481         
31482         this.fireEvent('afterupload', this, xhr);
31483         
31484     },
31485     
31486     xhrOnError : function(xhr)
31487     {
31488         Roo.log('xhr on error');
31489         
31490         var response = Roo.decode(xhr.responseText);
31491           
31492         Roo.log(response);
31493         
31494         this.arrange();
31495     },
31496     
31497     process : function(file)
31498     {
31499         if(this.fireEvent('process', this, file) !== false){
31500             if(this.editable && file.type.indexOf('image') != -1){
31501                 this.fireEvent('edit', this, file);
31502                 return;
31503             }
31504
31505             this.uploadStart(file, false);
31506
31507             return;
31508         }
31509         
31510     },
31511     
31512     uploadStart : function(file, crop)
31513     {
31514         this.xhr = new XMLHttpRequest();
31515         
31516         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31517             this.arrange();
31518             return;
31519         }
31520         
31521         file.xhr = this.xhr;
31522             
31523         this.managerEl.createChild({
31524             tag : 'div',
31525             cls : 'roo-document-manager-loading',
31526             cn : [
31527                 {
31528                     tag : 'div',
31529                     tooltip : file.name,
31530                     cls : 'roo-document-manager-thumb',
31531                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31532                 }
31533             ]
31534
31535         });
31536
31537         this.xhr.open(this.method, this.url, true);
31538         
31539         var headers = {
31540             "Accept": "application/json",
31541             "Cache-Control": "no-cache",
31542             "X-Requested-With": "XMLHttpRequest"
31543         };
31544         
31545         for (var headerName in headers) {
31546             var headerValue = headers[headerName];
31547             if (headerValue) {
31548                 this.xhr.setRequestHeader(headerName, headerValue);
31549             }
31550         }
31551         
31552         var _this = this;
31553         
31554         this.xhr.onload = function()
31555         {
31556             _this.xhrOnLoad(_this.xhr);
31557         }
31558         
31559         this.xhr.onerror = function()
31560         {
31561             _this.xhrOnError(_this.xhr);
31562         }
31563         
31564         var formData = new FormData();
31565
31566         formData.append('returnHTML', 'NO');
31567         
31568         if(crop){
31569             formData.append('crop', crop);
31570         }
31571         
31572         formData.append(this.paramName, file, file.name);
31573         
31574         var options = {
31575             file : file, 
31576             manually : false
31577         };
31578         
31579         if(this.fireEvent('prepare', this, formData, options) != false){
31580             
31581             if(options.manually){
31582                 return;
31583             }
31584             
31585             this.xhr.send(formData);
31586             return;
31587         };
31588         
31589         this.uploadCancel();
31590     },
31591     
31592     uploadCancel : function()
31593     {
31594         if (this.xhr) {
31595             this.xhr.abort();
31596         }
31597         
31598         this.delegates = [];
31599         
31600         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31601             el.remove();
31602         }, this);
31603         
31604         this.arrange();
31605     },
31606     
31607     renderPreview : function(file)
31608     {
31609         if(typeof(file.target) != 'undefined' && file.target){
31610             return file;
31611         }
31612         
31613         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31614         
31615         var previewEl = this.managerEl.createChild({
31616             tag : 'div',
31617             cls : 'roo-document-manager-preview',
31618             cn : [
31619                 {
31620                     tag : 'div',
31621                     tooltip : file[this.toolTipName],
31622                     cls : 'roo-document-manager-thumb',
31623                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31624                 },
31625                 {
31626                     tag : 'button',
31627                     cls : 'close',
31628                     html : '<i class="fa fa-times-circle"></i>'
31629                 }
31630             ]
31631         });
31632
31633         var close = previewEl.select('button.close', true).first();
31634
31635         close.on('click', this.onRemove, this, file);
31636
31637         file.target = previewEl;
31638
31639         var image = previewEl.select('img', true).first();
31640         
31641         var _this = this;
31642         
31643         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31644         
31645         image.on('click', this.onClick, this, file);
31646         
31647         this.fireEvent('previewrendered', this, file);
31648         
31649         return file;
31650         
31651     },
31652     
31653     onPreviewLoad : function(file, image)
31654     {
31655         if(typeof(file.target) == 'undefined' || !file.target){
31656             return;
31657         }
31658         
31659         var width = image.dom.naturalWidth || image.dom.width;
31660         var height = image.dom.naturalHeight || image.dom.height;
31661         
31662         if(!this.previewResize) {
31663             return;
31664         }
31665         
31666         if(width > height){
31667             file.target.addClass('wide');
31668             return;
31669         }
31670         
31671         file.target.addClass('tall');
31672         return;
31673         
31674     },
31675     
31676     uploadFromSource : function(file, crop)
31677     {
31678         this.xhr = new XMLHttpRequest();
31679         
31680         this.managerEl.createChild({
31681             tag : 'div',
31682             cls : 'roo-document-manager-loading',
31683             cn : [
31684                 {
31685                     tag : 'div',
31686                     tooltip : file.name,
31687                     cls : 'roo-document-manager-thumb',
31688                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31689                 }
31690             ]
31691
31692         });
31693
31694         this.xhr.open(this.method, this.url, true);
31695         
31696         var headers = {
31697             "Accept": "application/json",
31698             "Cache-Control": "no-cache",
31699             "X-Requested-With": "XMLHttpRequest"
31700         };
31701         
31702         for (var headerName in headers) {
31703             var headerValue = headers[headerName];
31704             if (headerValue) {
31705                 this.xhr.setRequestHeader(headerName, headerValue);
31706             }
31707         }
31708         
31709         var _this = this;
31710         
31711         this.xhr.onload = function()
31712         {
31713             _this.xhrOnLoad(_this.xhr);
31714         }
31715         
31716         this.xhr.onerror = function()
31717         {
31718             _this.xhrOnError(_this.xhr);
31719         }
31720         
31721         var formData = new FormData();
31722
31723         formData.append('returnHTML', 'NO');
31724         
31725         formData.append('crop', crop);
31726         
31727         if(typeof(file.filename) != 'undefined'){
31728             formData.append('filename', file.filename);
31729         }
31730         
31731         if(typeof(file.mimetype) != 'undefined'){
31732             formData.append('mimetype', file.mimetype);
31733         }
31734         
31735         Roo.log(formData);
31736         
31737         if(this.fireEvent('prepare', this, formData) != false){
31738             this.xhr.send(formData);
31739         };
31740     }
31741 });
31742
31743 /*
31744 * Licence: LGPL
31745 */
31746
31747 /**
31748  * @class Roo.bootstrap.DocumentViewer
31749  * @extends Roo.bootstrap.Component
31750  * Bootstrap DocumentViewer class
31751  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31752  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31753  * 
31754  * @constructor
31755  * Create a new DocumentViewer
31756  * @param {Object} config The config object
31757  */
31758
31759 Roo.bootstrap.DocumentViewer = function(config){
31760     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31761     
31762     this.addEvents({
31763         /**
31764          * @event initial
31765          * Fire after initEvent
31766          * @param {Roo.bootstrap.DocumentViewer} this
31767          */
31768         "initial" : true,
31769         /**
31770          * @event click
31771          * Fire after click
31772          * @param {Roo.bootstrap.DocumentViewer} this
31773          */
31774         "click" : true,
31775         /**
31776          * @event download
31777          * Fire after download button
31778          * @param {Roo.bootstrap.DocumentViewer} this
31779          */
31780         "download" : true,
31781         /**
31782          * @event trash
31783          * Fire after trash button
31784          * @param {Roo.bootstrap.DocumentViewer} this
31785          */
31786         "trash" : true
31787         
31788     });
31789 };
31790
31791 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31792     
31793     showDownload : true,
31794     
31795     showTrash : true,
31796     
31797     getAutoCreate : function()
31798     {
31799         var cfg = {
31800             tag : 'div',
31801             cls : 'roo-document-viewer',
31802             cn : [
31803                 {
31804                     tag : 'div',
31805                     cls : 'roo-document-viewer-body',
31806                     cn : [
31807                         {
31808                             tag : 'div',
31809                             cls : 'roo-document-viewer-thumb',
31810                             cn : [
31811                                 {
31812                                     tag : 'img',
31813                                     cls : 'roo-document-viewer-image'
31814                                 }
31815                             ]
31816                         }
31817                     ]
31818                 },
31819                 {
31820                     tag : 'div',
31821                     cls : 'roo-document-viewer-footer',
31822                     cn : {
31823                         tag : 'div',
31824                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31825                         cn : [
31826                             {
31827                                 tag : 'div',
31828                                 cls : 'btn-group roo-document-viewer-download',
31829                                 cn : [
31830                                     {
31831                                         tag : 'button',
31832                                         cls : 'btn btn-default',
31833                                         html : '<i class="fa fa-download"></i>'
31834                                     }
31835                                 ]
31836                             },
31837                             {
31838                                 tag : 'div',
31839                                 cls : 'btn-group roo-document-viewer-trash',
31840                                 cn : [
31841                                     {
31842                                         tag : 'button',
31843                                         cls : 'btn btn-default',
31844                                         html : '<i class="fa fa-trash"></i>'
31845                                     }
31846                                 ]
31847                             }
31848                         ]
31849                     }
31850                 }
31851             ]
31852         };
31853         
31854         return cfg;
31855     },
31856     
31857     initEvents : function()
31858     {
31859         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31860         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31861         
31862         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31863         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31864         
31865         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31866         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31867         
31868         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31869         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31870         
31871         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31872         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31873         
31874         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31875         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31876         
31877         this.bodyEl.on('click', this.onClick, this);
31878         this.downloadBtn.on('click', this.onDownload, this);
31879         this.trashBtn.on('click', this.onTrash, this);
31880         
31881         this.downloadBtn.hide();
31882         this.trashBtn.hide();
31883         
31884         if(this.showDownload){
31885             this.downloadBtn.show();
31886         }
31887         
31888         if(this.showTrash){
31889             this.trashBtn.show();
31890         }
31891         
31892         if(!this.showDownload && !this.showTrash) {
31893             this.footerEl.hide();
31894         }
31895         
31896     },
31897     
31898     initial : function()
31899     {
31900         this.fireEvent('initial', this);
31901         
31902     },
31903     
31904     onClick : function(e)
31905     {
31906         e.preventDefault();
31907         
31908         this.fireEvent('click', this);
31909     },
31910     
31911     onDownload : function(e)
31912     {
31913         e.preventDefault();
31914         
31915         this.fireEvent('download', this);
31916     },
31917     
31918     onTrash : function(e)
31919     {
31920         e.preventDefault();
31921         
31922         this.fireEvent('trash', this);
31923     }
31924     
31925 });
31926 /*
31927  * - LGPL
31928  *
31929  * nav progress bar
31930  * 
31931  */
31932
31933 /**
31934  * @class Roo.bootstrap.NavProgressBar
31935  * @extends Roo.bootstrap.Component
31936  * Bootstrap NavProgressBar class
31937  * 
31938  * @constructor
31939  * Create a new nav progress bar
31940  * @param {Object} config The config object
31941  */
31942
31943 Roo.bootstrap.NavProgressBar = function(config){
31944     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31945
31946     this.bullets = this.bullets || [];
31947    
31948 //    Roo.bootstrap.NavProgressBar.register(this);
31949      this.addEvents({
31950         /**
31951              * @event changed
31952              * Fires when the active item changes
31953              * @param {Roo.bootstrap.NavProgressBar} this
31954              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31955              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31956          */
31957         'changed': true
31958      });
31959     
31960 };
31961
31962 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31963     
31964     bullets : [],
31965     barItems : [],
31966     
31967     getAutoCreate : function()
31968     {
31969         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31970         
31971         cfg = {
31972             tag : 'div',
31973             cls : 'roo-navigation-bar-group',
31974             cn : [
31975                 {
31976                     tag : 'div',
31977                     cls : 'roo-navigation-top-bar'
31978                 },
31979                 {
31980                     tag : 'div',
31981                     cls : 'roo-navigation-bullets-bar',
31982                     cn : [
31983                         {
31984                             tag : 'ul',
31985                             cls : 'roo-navigation-bar'
31986                         }
31987                     ]
31988                 },
31989                 
31990                 {
31991                     tag : 'div',
31992                     cls : 'roo-navigation-bottom-bar'
31993                 }
31994             ]
31995             
31996         };
31997         
31998         return cfg;
31999         
32000     },
32001     
32002     initEvents: function() 
32003     {
32004         
32005     },
32006     
32007     onRender : function(ct, position) 
32008     {
32009         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32010         
32011         if(this.bullets.length){
32012             Roo.each(this.bullets, function(b){
32013                this.addItem(b);
32014             }, this);
32015         }
32016         
32017         this.format();
32018         
32019     },
32020     
32021     addItem : function(cfg)
32022     {
32023         var item = new Roo.bootstrap.NavProgressItem(cfg);
32024         
32025         item.parentId = this.id;
32026         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32027         
32028         if(cfg.html){
32029             var top = new Roo.bootstrap.Element({
32030                 tag : 'div',
32031                 cls : 'roo-navigation-bar-text'
32032             });
32033             
32034             var bottom = new Roo.bootstrap.Element({
32035                 tag : 'div',
32036                 cls : 'roo-navigation-bar-text'
32037             });
32038             
32039             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32040             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32041             
32042             var topText = new Roo.bootstrap.Element({
32043                 tag : 'span',
32044                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32045             });
32046             
32047             var bottomText = new Roo.bootstrap.Element({
32048                 tag : 'span',
32049                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32050             });
32051             
32052             topText.onRender(top.el, null);
32053             bottomText.onRender(bottom.el, null);
32054             
32055             item.topEl = top;
32056             item.bottomEl = bottom;
32057         }
32058         
32059         this.barItems.push(item);
32060         
32061         return item;
32062     },
32063     
32064     getActive : function()
32065     {
32066         var active = false;
32067         
32068         Roo.each(this.barItems, function(v){
32069             
32070             if (!v.isActive()) {
32071                 return;
32072             }
32073             
32074             active = v;
32075             return false;
32076             
32077         });
32078         
32079         return active;
32080     },
32081     
32082     setActiveItem : function(item)
32083     {
32084         var prev = false;
32085         
32086         Roo.each(this.barItems, function(v){
32087             if (v.rid == item.rid) {
32088                 return ;
32089             }
32090             
32091             if (v.isActive()) {
32092                 v.setActive(false);
32093                 prev = v;
32094             }
32095         });
32096
32097         item.setActive(true);
32098         
32099         this.fireEvent('changed', this, item, prev);
32100     },
32101     
32102     getBarItem: function(rid)
32103     {
32104         var ret = false;
32105         
32106         Roo.each(this.barItems, function(e) {
32107             if (e.rid != rid) {
32108                 return;
32109             }
32110             
32111             ret =  e;
32112             return false;
32113         });
32114         
32115         return ret;
32116     },
32117     
32118     indexOfItem : function(item)
32119     {
32120         var index = false;
32121         
32122         Roo.each(this.barItems, function(v, i){
32123             
32124             if (v.rid != item.rid) {
32125                 return;
32126             }
32127             
32128             index = i;
32129             return false
32130         });
32131         
32132         return index;
32133     },
32134     
32135     setActiveNext : function()
32136     {
32137         var i = this.indexOfItem(this.getActive());
32138         
32139         if (i > this.barItems.length) {
32140             return;
32141         }
32142         
32143         this.setActiveItem(this.barItems[i+1]);
32144     },
32145     
32146     setActivePrev : function()
32147     {
32148         var i = this.indexOfItem(this.getActive());
32149         
32150         if (i  < 1) {
32151             return;
32152         }
32153         
32154         this.setActiveItem(this.barItems[i-1]);
32155     },
32156     
32157     format : function()
32158     {
32159         if(!this.barItems.length){
32160             return;
32161         }
32162      
32163         var width = 100 / this.barItems.length;
32164         
32165         Roo.each(this.barItems, function(i){
32166             i.el.setStyle('width', width + '%');
32167             i.topEl.el.setStyle('width', width + '%');
32168             i.bottomEl.el.setStyle('width', width + '%');
32169         }, this);
32170         
32171     }
32172     
32173 });
32174 /*
32175  * - LGPL
32176  *
32177  * Nav Progress Item
32178  * 
32179  */
32180
32181 /**
32182  * @class Roo.bootstrap.NavProgressItem
32183  * @extends Roo.bootstrap.Component
32184  * Bootstrap NavProgressItem class
32185  * @cfg {String} rid the reference id
32186  * @cfg {Boolean} active (true|false) Is item active default false
32187  * @cfg {Boolean} disabled (true|false) Is item active default false
32188  * @cfg {String} html
32189  * @cfg {String} position (top|bottom) text position default bottom
32190  * @cfg {String} icon show icon instead of number
32191  * 
32192  * @constructor
32193  * Create a new NavProgressItem
32194  * @param {Object} config The config object
32195  */
32196 Roo.bootstrap.NavProgressItem = function(config){
32197     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32198     this.addEvents({
32199         // raw events
32200         /**
32201          * @event click
32202          * The raw click event for the entire grid.
32203          * @param {Roo.bootstrap.NavProgressItem} this
32204          * @param {Roo.EventObject} e
32205          */
32206         "click" : true
32207     });
32208    
32209 };
32210
32211 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32212     
32213     rid : '',
32214     active : false,
32215     disabled : false,
32216     html : '',
32217     position : 'bottom',
32218     icon : false,
32219     
32220     getAutoCreate : function()
32221     {
32222         var iconCls = 'roo-navigation-bar-item-icon';
32223         
32224         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32225         
32226         var cfg = {
32227             tag: 'li',
32228             cls: 'roo-navigation-bar-item',
32229             cn : [
32230                 {
32231                     tag : 'i',
32232                     cls : iconCls
32233                 }
32234             ]
32235         };
32236         
32237         if(this.active){
32238             cfg.cls += ' active';
32239         }
32240         if(this.disabled){
32241             cfg.cls += ' disabled';
32242         }
32243         
32244         return cfg;
32245     },
32246     
32247     disable : function()
32248     {
32249         this.setDisabled(true);
32250     },
32251     
32252     enable : function()
32253     {
32254         this.setDisabled(false);
32255     },
32256     
32257     initEvents: function() 
32258     {
32259         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32260         
32261         this.iconEl.on('click', this.onClick, this);
32262     },
32263     
32264     onClick : function(e)
32265     {
32266         e.preventDefault();
32267         
32268         if(this.disabled){
32269             return;
32270         }
32271         
32272         if(this.fireEvent('click', this, e) === false){
32273             return;
32274         };
32275         
32276         this.parent().setActiveItem(this);
32277     },
32278     
32279     isActive: function () 
32280     {
32281         return this.active;
32282     },
32283     
32284     setActive : function(state)
32285     {
32286         if(this.active == state){
32287             return;
32288         }
32289         
32290         this.active = state;
32291         
32292         if (state) {
32293             this.el.addClass('active');
32294             return;
32295         }
32296         
32297         this.el.removeClass('active');
32298         
32299         return;
32300     },
32301     
32302     setDisabled : function(state)
32303     {
32304         if(this.disabled == state){
32305             return;
32306         }
32307         
32308         this.disabled = state;
32309         
32310         if (state) {
32311             this.el.addClass('disabled');
32312             return;
32313         }
32314         
32315         this.el.removeClass('disabled');
32316     },
32317     
32318     tooltipEl : function()
32319     {
32320         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32321     }
32322 });
32323  
32324
32325  /*
32326  * - LGPL
32327  *
32328  * FieldLabel
32329  * 
32330  */
32331
32332 /**
32333  * @class Roo.bootstrap.FieldLabel
32334  * @extends Roo.bootstrap.Component
32335  * Bootstrap FieldLabel class
32336  * @cfg {String} html contents of the element
32337  * @cfg {String} tag tag of the element default label
32338  * @cfg {String} cls class of the element
32339  * @cfg {String} target label target 
32340  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32341  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32342  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32343  * @cfg {String} iconTooltip default "This field is required"
32344  * @cfg {String} indicatorpos (left|right) default left
32345  * 
32346  * @constructor
32347  * Create a new FieldLabel
32348  * @param {Object} config The config object
32349  */
32350
32351 Roo.bootstrap.FieldLabel = function(config){
32352     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32353     
32354     this.addEvents({
32355             /**
32356              * @event invalid
32357              * Fires after the field has been marked as invalid.
32358              * @param {Roo.form.FieldLabel} this
32359              * @param {String} msg The validation message
32360              */
32361             invalid : true,
32362             /**
32363              * @event valid
32364              * Fires after the field has been validated with no errors.
32365              * @param {Roo.form.FieldLabel} this
32366              */
32367             valid : true
32368         });
32369 };
32370
32371 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32372     
32373     tag: 'label',
32374     cls: '',
32375     html: '',
32376     target: '',
32377     allowBlank : true,
32378     invalidClass : 'has-warning',
32379     validClass : 'has-success',
32380     iconTooltip : 'This field is required',
32381     indicatorpos : 'left',
32382     
32383     getAutoCreate : function(){
32384         
32385         var cls = "";
32386         if (!this.allowBlank) {
32387             cls  = "visible";
32388         }
32389         
32390         var cfg = {
32391             tag : this.tag,
32392             cls : 'roo-bootstrap-field-label ' + this.cls,
32393             for : this.target,
32394             cn : [
32395                 {
32396                     tag : 'i',
32397                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32398                     tooltip : this.iconTooltip
32399                 },
32400                 {
32401                     tag : 'span',
32402                     html : this.html
32403                 }
32404             ] 
32405         };
32406         
32407         if(this.indicatorpos == 'right'){
32408             var cfg = {
32409                 tag : this.tag,
32410                 cls : 'roo-bootstrap-field-label ' + this.cls,
32411                 for : this.target,
32412                 cn : [
32413                     {
32414                         tag : 'span',
32415                         html : this.html
32416                     },
32417                     {
32418                         tag : 'i',
32419                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32420                         tooltip : this.iconTooltip
32421                     }
32422                 ] 
32423             };
32424         }
32425         
32426         return cfg;
32427     },
32428     
32429     initEvents: function() 
32430     {
32431         Roo.bootstrap.Element.superclass.initEvents.call(this);
32432         
32433         this.indicator = this.indicatorEl();
32434         
32435         if(this.indicator){
32436             this.indicator.removeClass('visible');
32437             this.indicator.addClass('invisible');
32438         }
32439         
32440         Roo.bootstrap.FieldLabel.register(this);
32441     },
32442     
32443     indicatorEl : function()
32444     {
32445         var indicator = this.el.select('i.roo-required-indicator',true).first();
32446         
32447         if(!indicator){
32448             return false;
32449         }
32450         
32451         return indicator;
32452         
32453     },
32454     
32455     /**
32456      * Mark this field as valid
32457      */
32458     markValid : function()
32459     {
32460         if(this.indicator){
32461             this.indicator.removeClass('visible');
32462             this.indicator.addClass('invisible');
32463         }
32464         if (Roo.bootstrap.version == 3) {
32465             this.el.removeClass(this.invalidClass);
32466             this.el.addClass(this.validClass);
32467         } else {
32468             this.el.removeClass('is-invalid');
32469             this.el.addClass('is-valid');
32470         }
32471         
32472         
32473         this.fireEvent('valid', this);
32474     },
32475     
32476     /**
32477      * Mark this field as invalid
32478      * @param {String} msg The validation message
32479      */
32480     markInvalid : function(msg)
32481     {
32482         if(this.indicator){
32483             this.indicator.removeClass('invisible');
32484             this.indicator.addClass('visible');
32485         }
32486           if (Roo.bootstrap.version == 3) {
32487             this.el.removeClass(this.validClass);
32488             this.el.addClass(this.invalidClass);
32489         } else {
32490             this.el.removeClass('is-valid');
32491             this.el.addClass('is-invalid');
32492         }
32493         
32494         
32495         this.fireEvent('invalid', this, msg);
32496     }
32497     
32498    
32499 });
32500
32501 Roo.apply(Roo.bootstrap.FieldLabel, {
32502     
32503     groups: {},
32504     
32505      /**
32506     * register a FieldLabel Group
32507     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32508     */
32509     register : function(label)
32510     {
32511         if(this.groups.hasOwnProperty(label.target)){
32512             return;
32513         }
32514      
32515         this.groups[label.target] = label;
32516         
32517     },
32518     /**
32519     * fetch a FieldLabel Group based on the target
32520     * @param {string} target
32521     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32522     */
32523     get: function(target) {
32524         if (typeof(this.groups[target]) == 'undefined') {
32525             return false;
32526         }
32527         
32528         return this.groups[target] ;
32529     }
32530 });
32531
32532  
32533
32534  /*
32535  * - LGPL
32536  *
32537  * page DateSplitField.
32538  * 
32539  */
32540
32541
32542 /**
32543  * @class Roo.bootstrap.DateSplitField
32544  * @extends Roo.bootstrap.Component
32545  * Bootstrap DateSplitField class
32546  * @cfg {string} fieldLabel - the label associated
32547  * @cfg {Number} labelWidth set the width of label (0-12)
32548  * @cfg {String} labelAlign (top|left)
32549  * @cfg {Boolean} dayAllowBlank (true|false) default false
32550  * @cfg {Boolean} monthAllowBlank (true|false) default false
32551  * @cfg {Boolean} yearAllowBlank (true|false) default false
32552  * @cfg {string} dayPlaceholder 
32553  * @cfg {string} monthPlaceholder
32554  * @cfg {string} yearPlaceholder
32555  * @cfg {string} dayFormat default 'd'
32556  * @cfg {string} monthFormat default 'm'
32557  * @cfg {string} yearFormat default 'Y'
32558  * @cfg {Number} labellg set the width of label (1-12)
32559  * @cfg {Number} labelmd set the width of label (1-12)
32560  * @cfg {Number} labelsm set the width of label (1-12)
32561  * @cfg {Number} labelxs set the width of label (1-12)
32562
32563  *     
32564  * @constructor
32565  * Create a new DateSplitField
32566  * @param {Object} config The config object
32567  */
32568
32569 Roo.bootstrap.DateSplitField = function(config){
32570     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32571     
32572     this.addEvents({
32573         // raw events
32574          /**
32575          * @event years
32576          * getting the data of years
32577          * @param {Roo.bootstrap.DateSplitField} this
32578          * @param {Object} years
32579          */
32580         "years" : true,
32581         /**
32582          * @event days
32583          * getting the data of days
32584          * @param {Roo.bootstrap.DateSplitField} this
32585          * @param {Object} days
32586          */
32587         "days" : true,
32588         /**
32589          * @event invalid
32590          * Fires after the field has been marked as invalid.
32591          * @param {Roo.form.Field} this
32592          * @param {String} msg The validation message
32593          */
32594         invalid : true,
32595        /**
32596          * @event valid
32597          * Fires after the field has been validated with no errors.
32598          * @param {Roo.form.Field} this
32599          */
32600         valid : true
32601     });
32602 };
32603
32604 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32605     
32606     fieldLabel : '',
32607     labelAlign : 'top',
32608     labelWidth : 3,
32609     dayAllowBlank : false,
32610     monthAllowBlank : false,
32611     yearAllowBlank : false,
32612     dayPlaceholder : '',
32613     monthPlaceholder : '',
32614     yearPlaceholder : '',
32615     dayFormat : 'd',
32616     monthFormat : 'm',
32617     yearFormat : 'Y',
32618     isFormField : true,
32619     labellg : 0,
32620     labelmd : 0,
32621     labelsm : 0,
32622     labelxs : 0,
32623     
32624     getAutoCreate : function()
32625     {
32626         var cfg = {
32627             tag : 'div',
32628             cls : 'row roo-date-split-field-group',
32629             cn : [
32630                 {
32631                     tag : 'input',
32632                     type : 'hidden',
32633                     cls : 'form-hidden-field roo-date-split-field-group-value',
32634                     name : this.name
32635                 }
32636             ]
32637         };
32638         
32639         var labelCls = 'col-md-12';
32640         var contentCls = 'col-md-4';
32641         
32642         if(this.fieldLabel){
32643             
32644             var label = {
32645                 tag : 'div',
32646                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32647                 cn : [
32648                     {
32649                         tag : 'label',
32650                         html : this.fieldLabel
32651                     }
32652                 ]
32653             };
32654             
32655             if(this.labelAlign == 'left'){
32656             
32657                 if(this.labelWidth > 12){
32658                     label.style = "width: " + this.labelWidth + 'px';
32659                 }
32660
32661                 if(this.labelWidth < 13 && this.labelmd == 0){
32662                     this.labelmd = this.labelWidth;
32663                 }
32664
32665                 if(this.labellg > 0){
32666                     labelCls = ' col-lg-' + this.labellg;
32667                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32668                 }
32669
32670                 if(this.labelmd > 0){
32671                     labelCls = ' col-md-' + this.labelmd;
32672                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32673                 }
32674
32675                 if(this.labelsm > 0){
32676                     labelCls = ' col-sm-' + this.labelsm;
32677                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32678                 }
32679
32680                 if(this.labelxs > 0){
32681                     labelCls = ' col-xs-' + this.labelxs;
32682                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32683                 }
32684             }
32685             
32686             label.cls += ' ' + labelCls;
32687             
32688             cfg.cn.push(label);
32689         }
32690         
32691         Roo.each(['day', 'month', 'year'], function(t){
32692             cfg.cn.push({
32693                 tag : 'div',
32694                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32695             });
32696         }, this);
32697         
32698         return cfg;
32699     },
32700     
32701     inputEl: function ()
32702     {
32703         return this.el.select('.roo-date-split-field-group-value', true).first();
32704     },
32705     
32706     onRender : function(ct, position) 
32707     {
32708         var _this = this;
32709         
32710         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32711         
32712         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32713         
32714         this.dayField = new Roo.bootstrap.ComboBox({
32715             allowBlank : this.dayAllowBlank,
32716             alwaysQuery : true,
32717             displayField : 'value',
32718             editable : false,
32719             fieldLabel : '',
32720             forceSelection : true,
32721             mode : 'local',
32722             placeholder : this.dayPlaceholder,
32723             selectOnFocus : true,
32724             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32725             triggerAction : 'all',
32726             typeAhead : true,
32727             valueField : 'value',
32728             store : new Roo.data.SimpleStore({
32729                 data : (function() {    
32730                     var days = [];
32731                     _this.fireEvent('days', _this, days);
32732                     return days;
32733                 })(),
32734                 fields : [ 'value' ]
32735             }),
32736             listeners : {
32737                 select : function (_self, record, index)
32738                 {
32739                     _this.setValue(_this.getValue());
32740                 }
32741             }
32742         });
32743
32744         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32745         
32746         this.monthField = new Roo.bootstrap.MonthField({
32747             after : '<i class=\"fa fa-calendar\"></i>',
32748             allowBlank : this.monthAllowBlank,
32749             placeholder : this.monthPlaceholder,
32750             readOnly : true,
32751             listeners : {
32752                 render : function (_self)
32753                 {
32754                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32755                         e.preventDefault();
32756                         _self.focus();
32757                     });
32758                 },
32759                 select : function (_self, oldvalue, newvalue)
32760                 {
32761                     _this.setValue(_this.getValue());
32762                 }
32763             }
32764         });
32765         
32766         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32767         
32768         this.yearField = new Roo.bootstrap.ComboBox({
32769             allowBlank : this.yearAllowBlank,
32770             alwaysQuery : true,
32771             displayField : 'value',
32772             editable : false,
32773             fieldLabel : '',
32774             forceSelection : true,
32775             mode : 'local',
32776             placeholder : this.yearPlaceholder,
32777             selectOnFocus : true,
32778             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32779             triggerAction : 'all',
32780             typeAhead : true,
32781             valueField : 'value',
32782             store : new Roo.data.SimpleStore({
32783                 data : (function() {
32784                     var years = [];
32785                     _this.fireEvent('years', _this, years);
32786                     return years;
32787                 })(),
32788                 fields : [ 'value' ]
32789             }),
32790             listeners : {
32791                 select : function (_self, record, index)
32792                 {
32793                     _this.setValue(_this.getValue());
32794                 }
32795             }
32796         });
32797
32798         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32799     },
32800     
32801     setValue : function(v, format)
32802     {
32803         this.inputEl.dom.value = v;
32804         
32805         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32806         
32807         var d = Date.parseDate(v, f);
32808         
32809         if(!d){
32810             this.validate();
32811             return;
32812         }
32813         
32814         this.setDay(d.format(this.dayFormat));
32815         this.setMonth(d.format(this.monthFormat));
32816         this.setYear(d.format(this.yearFormat));
32817         
32818         this.validate();
32819         
32820         return;
32821     },
32822     
32823     setDay : function(v)
32824     {
32825         this.dayField.setValue(v);
32826         this.inputEl.dom.value = this.getValue();
32827         this.validate();
32828         return;
32829     },
32830     
32831     setMonth : function(v)
32832     {
32833         this.monthField.setValue(v, true);
32834         this.inputEl.dom.value = this.getValue();
32835         this.validate();
32836         return;
32837     },
32838     
32839     setYear : function(v)
32840     {
32841         this.yearField.setValue(v);
32842         this.inputEl.dom.value = this.getValue();
32843         this.validate();
32844         return;
32845     },
32846     
32847     getDay : function()
32848     {
32849         return this.dayField.getValue();
32850     },
32851     
32852     getMonth : function()
32853     {
32854         return this.monthField.getValue();
32855     },
32856     
32857     getYear : function()
32858     {
32859         return this.yearField.getValue();
32860     },
32861     
32862     getValue : function()
32863     {
32864         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32865         
32866         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32867         
32868         return date;
32869     },
32870     
32871     reset : function()
32872     {
32873         this.setDay('');
32874         this.setMonth('');
32875         this.setYear('');
32876         this.inputEl.dom.value = '';
32877         this.validate();
32878         return;
32879     },
32880     
32881     validate : function()
32882     {
32883         var d = this.dayField.validate();
32884         var m = this.monthField.validate();
32885         var y = this.yearField.validate();
32886         
32887         var valid = true;
32888         
32889         if(
32890                 (!this.dayAllowBlank && !d) ||
32891                 (!this.monthAllowBlank && !m) ||
32892                 (!this.yearAllowBlank && !y)
32893         ){
32894             valid = false;
32895         }
32896         
32897         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32898             return valid;
32899         }
32900         
32901         if(valid){
32902             this.markValid();
32903             return valid;
32904         }
32905         
32906         this.markInvalid();
32907         
32908         return valid;
32909     },
32910     
32911     markValid : function()
32912     {
32913         
32914         var label = this.el.select('label', true).first();
32915         var icon = this.el.select('i.fa-star', true).first();
32916
32917         if(label && icon){
32918             icon.remove();
32919         }
32920         
32921         this.fireEvent('valid', this);
32922     },
32923     
32924      /**
32925      * Mark this field as invalid
32926      * @param {String} msg The validation message
32927      */
32928     markInvalid : function(msg)
32929     {
32930         
32931         var label = this.el.select('label', true).first();
32932         var icon = this.el.select('i.fa-star', true).first();
32933
32934         if(label && !icon){
32935             this.el.select('.roo-date-split-field-label', true).createChild({
32936                 tag : 'i',
32937                 cls : 'text-danger fa fa-lg fa-star',
32938                 tooltip : 'This field is required',
32939                 style : 'margin-right:5px;'
32940             }, label, true);
32941         }
32942         
32943         this.fireEvent('invalid', this, msg);
32944     },
32945     
32946     clearInvalid : function()
32947     {
32948         var label = this.el.select('label', true).first();
32949         var icon = this.el.select('i.fa-star', true).first();
32950
32951         if(label && icon){
32952             icon.remove();
32953         }
32954         
32955         this.fireEvent('valid', this);
32956     },
32957     
32958     getName: function()
32959     {
32960         return this.name;
32961     }
32962     
32963 });
32964
32965  /**
32966  *
32967  * This is based on 
32968  * http://masonry.desandro.com
32969  *
32970  * The idea is to render all the bricks based on vertical width...
32971  *
32972  * The original code extends 'outlayer' - we might need to use that....
32973  * 
32974  */
32975
32976
32977 /**
32978  * @class Roo.bootstrap.LayoutMasonry
32979  * @extends Roo.bootstrap.Component
32980  * Bootstrap Layout Masonry class
32981  * 
32982  * @constructor
32983  * Create a new Element
32984  * @param {Object} config The config object
32985  */
32986
32987 Roo.bootstrap.LayoutMasonry = function(config){
32988     
32989     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32990     
32991     this.bricks = [];
32992     
32993     Roo.bootstrap.LayoutMasonry.register(this);
32994     
32995     this.addEvents({
32996         // raw events
32997         /**
32998          * @event layout
32999          * Fire after layout the items
33000          * @param {Roo.bootstrap.LayoutMasonry} this
33001          * @param {Roo.EventObject} e
33002          */
33003         "layout" : true
33004     });
33005     
33006 };
33007
33008 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33009     
33010     /**
33011      * @cfg {Boolean} isLayoutInstant = no animation?
33012      */   
33013     isLayoutInstant : false, // needed?
33014    
33015     /**
33016      * @cfg {Number} boxWidth  width of the columns
33017      */   
33018     boxWidth : 450,
33019     
33020       /**
33021      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33022      */   
33023     boxHeight : 0,
33024     
33025     /**
33026      * @cfg {Number} padWidth padding below box..
33027      */   
33028     padWidth : 10, 
33029     
33030     /**
33031      * @cfg {Number} gutter gutter width..
33032      */   
33033     gutter : 10,
33034     
33035      /**
33036      * @cfg {Number} maxCols maximum number of columns
33037      */   
33038     
33039     maxCols: 0,
33040     
33041     /**
33042      * @cfg {Boolean} isAutoInitial defalut true
33043      */   
33044     isAutoInitial : true, 
33045     
33046     containerWidth: 0,
33047     
33048     /**
33049      * @cfg {Boolean} isHorizontal defalut false
33050      */   
33051     isHorizontal : false, 
33052
33053     currentSize : null,
33054     
33055     tag: 'div',
33056     
33057     cls: '',
33058     
33059     bricks: null, //CompositeElement
33060     
33061     cols : 1,
33062     
33063     _isLayoutInited : false,
33064     
33065 //    isAlternative : false, // only use for vertical layout...
33066     
33067     /**
33068      * @cfg {Number} alternativePadWidth padding below box..
33069      */   
33070     alternativePadWidth : 50,
33071     
33072     selectedBrick : [],
33073     
33074     getAutoCreate : function(){
33075         
33076         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33077         
33078         var cfg = {
33079             tag: this.tag,
33080             cls: 'blog-masonary-wrapper ' + this.cls,
33081             cn : {
33082                 cls : 'mas-boxes masonary'
33083             }
33084         };
33085         
33086         return cfg;
33087     },
33088     
33089     getChildContainer: function( )
33090     {
33091         if (this.boxesEl) {
33092             return this.boxesEl;
33093         }
33094         
33095         this.boxesEl = this.el.select('.mas-boxes').first();
33096         
33097         return this.boxesEl;
33098     },
33099     
33100     
33101     initEvents : function()
33102     {
33103         var _this = this;
33104         
33105         if(this.isAutoInitial){
33106             Roo.log('hook children rendered');
33107             this.on('childrenrendered', function() {
33108                 Roo.log('children rendered');
33109                 _this.initial();
33110             } ,this);
33111         }
33112     },
33113     
33114     initial : function()
33115     {
33116         this.selectedBrick = [];
33117         
33118         this.currentSize = this.el.getBox(true);
33119         
33120         Roo.EventManager.onWindowResize(this.resize, this); 
33121
33122         if(!this.isAutoInitial){
33123             this.layout();
33124             return;
33125         }
33126         
33127         this.layout();
33128         
33129         return;
33130         //this.layout.defer(500,this);
33131         
33132     },
33133     
33134     resize : function()
33135     {
33136         var cs = this.el.getBox(true);
33137         
33138         if (
33139                 this.currentSize.width == cs.width && 
33140                 this.currentSize.x == cs.x && 
33141                 this.currentSize.height == cs.height && 
33142                 this.currentSize.y == cs.y 
33143         ) {
33144             Roo.log("no change in with or X or Y");
33145             return;
33146         }
33147         
33148         this.currentSize = cs;
33149         
33150         this.layout();
33151         
33152     },
33153     
33154     layout : function()
33155     {   
33156         this._resetLayout();
33157         
33158         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33159         
33160         this.layoutItems( isInstant );
33161       
33162         this._isLayoutInited = true;
33163         
33164         this.fireEvent('layout', this);
33165         
33166     },
33167     
33168     _resetLayout : function()
33169     {
33170         if(this.isHorizontal){
33171             this.horizontalMeasureColumns();
33172             return;
33173         }
33174         
33175         this.verticalMeasureColumns();
33176         
33177     },
33178     
33179     verticalMeasureColumns : function()
33180     {
33181         this.getContainerWidth();
33182         
33183 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33184 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33185 //            return;
33186 //        }
33187         
33188         var boxWidth = this.boxWidth + this.padWidth;
33189         
33190         if(this.containerWidth < this.boxWidth){
33191             boxWidth = this.containerWidth
33192         }
33193         
33194         var containerWidth = this.containerWidth;
33195         
33196         var cols = Math.floor(containerWidth / boxWidth);
33197         
33198         this.cols = Math.max( cols, 1 );
33199         
33200         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33201         
33202         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33203         
33204         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33205         
33206         this.colWidth = boxWidth + avail - this.padWidth;
33207         
33208         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33209         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33210     },
33211     
33212     horizontalMeasureColumns : function()
33213     {
33214         this.getContainerWidth();
33215         
33216         var boxWidth = this.boxWidth;
33217         
33218         if(this.containerWidth < boxWidth){
33219             boxWidth = this.containerWidth;
33220         }
33221         
33222         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33223         
33224         this.el.setHeight(boxWidth);
33225         
33226     },
33227     
33228     getContainerWidth : function()
33229     {
33230         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33231     },
33232     
33233     layoutItems : function( isInstant )
33234     {
33235         Roo.log(this.bricks);
33236         
33237         var items = Roo.apply([], this.bricks);
33238         
33239         if(this.isHorizontal){
33240             this._horizontalLayoutItems( items , isInstant );
33241             return;
33242         }
33243         
33244 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33245 //            this._verticalAlternativeLayoutItems( items , isInstant );
33246 //            return;
33247 //        }
33248         
33249         this._verticalLayoutItems( items , isInstant );
33250         
33251     },
33252     
33253     _verticalLayoutItems : function ( items , isInstant)
33254     {
33255         if ( !items || !items.length ) {
33256             return;
33257         }
33258         
33259         var standard = [
33260             ['xs', 'xs', 'xs', 'tall'],
33261             ['xs', 'xs', 'tall'],
33262             ['xs', 'xs', 'sm'],
33263             ['xs', 'xs', 'xs'],
33264             ['xs', 'tall'],
33265             ['xs', 'sm'],
33266             ['xs', 'xs'],
33267             ['xs'],
33268             
33269             ['sm', 'xs', 'xs'],
33270             ['sm', 'xs'],
33271             ['sm'],
33272             
33273             ['tall', 'xs', 'xs', 'xs'],
33274             ['tall', 'xs', 'xs'],
33275             ['tall', 'xs'],
33276             ['tall']
33277             
33278         ];
33279         
33280         var queue = [];
33281         
33282         var boxes = [];
33283         
33284         var box = [];
33285         
33286         Roo.each(items, function(item, k){
33287             
33288             switch (item.size) {
33289                 // these layouts take up a full box,
33290                 case 'md' :
33291                 case 'md-left' :
33292                 case 'md-right' :
33293                 case 'wide' :
33294                     
33295                     if(box.length){
33296                         boxes.push(box);
33297                         box = [];
33298                     }
33299                     
33300                     boxes.push([item]);
33301                     
33302                     break;
33303                     
33304                 case 'xs' :
33305                 case 'sm' :
33306                 case 'tall' :
33307                     
33308                     box.push(item);
33309                     
33310                     break;
33311                 default :
33312                     break;
33313                     
33314             }
33315             
33316         }, this);
33317         
33318         if(box.length){
33319             boxes.push(box);
33320             box = [];
33321         }
33322         
33323         var filterPattern = function(box, length)
33324         {
33325             if(!box.length){
33326                 return;
33327             }
33328             
33329             var match = false;
33330             
33331             var pattern = box.slice(0, length);
33332             
33333             var format = [];
33334             
33335             Roo.each(pattern, function(i){
33336                 format.push(i.size);
33337             }, this);
33338             
33339             Roo.each(standard, function(s){
33340                 
33341                 if(String(s) != String(format)){
33342                     return;
33343                 }
33344                 
33345                 match = true;
33346                 return false;
33347                 
33348             }, this);
33349             
33350             if(!match && length == 1){
33351                 return;
33352             }
33353             
33354             if(!match){
33355                 filterPattern(box, length - 1);
33356                 return;
33357             }
33358                 
33359             queue.push(pattern);
33360
33361             box = box.slice(length, box.length);
33362
33363             filterPattern(box, 4);
33364
33365             return;
33366             
33367         }
33368         
33369         Roo.each(boxes, function(box, k){
33370             
33371             if(!box.length){
33372                 return;
33373             }
33374             
33375             if(box.length == 1){
33376                 queue.push(box);
33377                 return;
33378             }
33379             
33380             filterPattern(box, 4);
33381             
33382         }, this);
33383         
33384         this._processVerticalLayoutQueue( queue, isInstant );
33385         
33386     },
33387     
33388 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33389 //    {
33390 //        if ( !items || !items.length ) {
33391 //            return;
33392 //        }
33393 //
33394 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33395 //        
33396 //    },
33397     
33398     _horizontalLayoutItems : function ( items , isInstant)
33399     {
33400         if ( !items || !items.length || items.length < 3) {
33401             return;
33402         }
33403         
33404         items.reverse();
33405         
33406         var eItems = items.slice(0, 3);
33407         
33408         items = items.slice(3, items.length);
33409         
33410         var standard = [
33411             ['xs', 'xs', 'xs', 'wide'],
33412             ['xs', 'xs', 'wide'],
33413             ['xs', 'xs', 'sm'],
33414             ['xs', 'xs', 'xs'],
33415             ['xs', 'wide'],
33416             ['xs', 'sm'],
33417             ['xs', 'xs'],
33418             ['xs'],
33419             
33420             ['sm', 'xs', 'xs'],
33421             ['sm', 'xs'],
33422             ['sm'],
33423             
33424             ['wide', 'xs', 'xs', 'xs'],
33425             ['wide', 'xs', 'xs'],
33426             ['wide', 'xs'],
33427             ['wide'],
33428             
33429             ['wide-thin']
33430         ];
33431         
33432         var queue = [];
33433         
33434         var boxes = [];
33435         
33436         var box = [];
33437         
33438         Roo.each(items, function(item, k){
33439             
33440             switch (item.size) {
33441                 case 'md' :
33442                 case 'md-left' :
33443                 case 'md-right' :
33444                 case 'tall' :
33445                     
33446                     if(box.length){
33447                         boxes.push(box);
33448                         box = [];
33449                     }
33450                     
33451                     boxes.push([item]);
33452                     
33453                     break;
33454                     
33455                 case 'xs' :
33456                 case 'sm' :
33457                 case 'wide' :
33458                 case 'wide-thin' :
33459                     
33460                     box.push(item);
33461                     
33462                     break;
33463                 default :
33464                     break;
33465                     
33466             }
33467             
33468         }, this);
33469         
33470         if(box.length){
33471             boxes.push(box);
33472             box = [];
33473         }
33474         
33475         var filterPattern = function(box, length)
33476         {
33477             if(!box.length){
33478                 return;
33479             }
33480             
33481             var match = false;
33482             
33483             var pattern = box.slice(0, length);
33484             
33485             var format = [];
33486             
33487             Roo.each(pattern, function(i){
33488                 format.push(i.size);
33489             }, this);
33490             
33491             Roo.each(standard, function(s){
33492                 
33493                 if(String(s) != String(format)){
33494                     return;
33495                 }
33496                 
33497                 match = true;
33498                 return false;
33499                 
33500             }, this);
33501             
33502             if(!match && length == 1){
33503                 return;
33504             }
33505             
33506             if(!match){
33507                 filterPattern(box, length - 1);
33508                 return;
33509             }
33510                 
33511             queue.push(pattern);
33512
33513             box = box.slice(length, box.length);
33514
33515             filterPattern(box, 4);
33516
33517             return;
33518             
33519         }
33520         
33521         Roo.each(boxes, function(box, k){
33522             
33523             if(!box.length){
33524                 return;
33525             }
33526             
33527             if(box.length == 1){
33528                 queue.push(box);
33529                 return;
33530             }
33531             
33532             filterPattern(box, 4);
33533             
33534         }, this);
33535         
33536         
33537         var prune = [];
33538         
33539         var pos = this.el.getBox(true);
33540         
33541         var minX = pos.x;
33542         
33543         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33544         
33545         var hit_end = false;
33546         
33547         Roo.each(queue, function(box){
33548             
33549             if(hit_end){
33550                 
33551                 Roo.each(box, function(b){
33552                 
33553                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33554                     b.el.hide();
33555
33556                 }, this);
33557
33558                 return;
33559             }
33560             
33561             var mx = 0;
33562             
33563             Roo.each(box, function(b){
33564                 
33565                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33566                 b.el.show();
33567
33568                 mx = Math.max(mx, b.x);
33569                 
33570             }, this);
33571             
33572             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33573             
33574             if(maxX < minX){
33575                 
33576                 Roo.each(box, function(b){
33577                 
33578                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33579                     b.el.hide();
33580                     
33581                 }, this);
33582                 
33583                 hit_end = true;
33584                 
33585                 return;
33586             }
33587             
33588             prune.push(box);
33589             
33590         }, this);
33591         
33592         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33593     },
33594     
33595     /** Sets position of item in DOM
33596     * @param {Element} item
33597     * @param {Number} x - horizontal position
33598     * @param {Number} y - vertical position
33599     * @param {Boolean} isInstant - disables transitions
33600     */
33601     _processVerticalLayoutQueue : function( queue, isInstant )
33602     {
33603         var pos = this.el.getBox(true);
33604         var x = pos.x;
33605         var y = pos.y;
33606         var maxY = [];
33607         
33608         for (var i = 0; i < this.cols; i++){
33609             maxY[i] = pos.y;
33610         }
33611         
33612         Roo.each(queue, function(box, k){
33613             
33614             var col = k % this.cols;
33615             
33616             Roo.each(box, function(b,kk){
33617                 
33618                 b.el.position('absolute');
33619                 
33620                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33621                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33622                 
33623                 if(b.size == 'md-left' || b.size == 'md-right'){
33624                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33625                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33626                 }
33627                 
33628                 b.el.setWidth(width);
33629                 b.el.setHeight(height);
33630                 // iframe?
33631                 b.el.select('iframe',true).setSize(width,height);
33632                 
33633             }, this);
33634             
33635             for (var i = 0; i < this.cols; i++){
33636                 
33637                 if(maxY[i] < maxY[col]){
33638                     col = i;
33639                     continue;
33640                 }
33641                 
33642                 col = Math.min(col, i);
33643                 
33644             }
33645             
33646             x = pos.x + col * (this.colWidth + this.padWidth);
33647             
33648             y = maxY[col];
33649             
33650             var positions = [];
33651             
33652             switch (box.length){
33653                 case 1 :
33654                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33655                     break;
33656                 case 2 :
33657                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33658                     break;
33659                 case 3 :
33660                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33661                     break;
33662                 case 4 :
33663                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33664                     break;
33665                 default :
33666                     break;
33667             }
33668             
33669             Roo.each(box, function(b,kk){
33670                 
33671                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33672                 
33673                 var sz = b.el.getSize();
33674                 
33675                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33676                 
33677             }, this);
33678             
33679         }, this);
33680         
33681         var mY = 0;
33682         
33683         for (var i = 0; i < this.cols; i++){
33684             mY = Math.max(mY, maxY[i]);
33685         }
33686         
33687         this.el.setHeight(mY - pos.y);
33688         
33689     },
33690     
33691 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33692 //    {
33693 //        var pos = this.el.getBox(true);
33694 //        var x = pos.x;
33695 //        var y = pos.y;
33696 //        var maxX = pos.right;
33697 //        
33698 //        var maxHeight = 0;
33699 //        
33700 //        Roo.each(items, function(item, k){
33701 //            
33702 //            var c = k % 2;
33703 //            
33704 //            item.el.position('absolute');
33705 //                
33706 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33707 //
33708 //            item.el.setWidth(width);
33709 //
33710 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33711 //
33712 //            item.el.setHeight(height);
33713 //            
33714 //            if(c == 0){
33715 //                item.el.setXY([x, y], isInstant ? false : true);
33716 //            } else {
33717 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33718 //            }
33719 //            
33720 //            y = y + height + this.alternativePadWidth;
33721 //            
33722 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33723 //            
33724 //        }, this);
33725 //        
33726 //        this.el.setHeight(maxHeight);
33727 //        
33728 //    },
33729     
33730     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33731     {
33732         var pos = this.el.getBox(true);
33733         
33734         var minX = pos.x;
33735         var minY = pos.y;
33736         
33737         var maxX = pos.right;
33738         
33739         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33740         
33741         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33742         
33743         Roo.each(queue, function(box, k){
33744             
33745             Roo.each(box, function(b, kk){
33746                 
33747                 b.el.position('absolute');
33748                 
33749                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33750                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33751                 
33752                 if(b.size == 'md-left' || b.size == 'md-right'){
33753                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33754                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33755                 }
33756                 
33757                 b.el.setWidth(width);
33758                 b.el.setHeight(height);
33759                 
33760             }, this);
33761             
33762             if(!box.length){
33763                 return;
33764             }
33765             
33766             var positions = [];
33767             
33768             switch (box.length){
33769                 case 1 :
33770                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33771                     break;
33772                 case 2 :
33773                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33774                     break;
33775                 case 3 :
33776                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33777                     break;
33778                 case 4 :
33779                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33780                     break;
33781                 default :
33782                     break;
33783             }
33784             
33785             Roo.each(box, function(b,kk){
33786                 
33787                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33788                 
33789                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33790                 
33791             }, this);
33792             
33793         }, this);
33794         
33795     },
33796     
33797     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33798     {
33799         Roo.each(eItems, function(b,k){
33800             
33801             b.size = (k == 0) ? 'sm' : 'xs';
33802             b.x = (k == 0) ? 2 : 1;
33803             b.y = (k == 0) ? 2 : 1;
33804             
33805             b.el.position('absolute');
33806             
33807             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33808                 
33809             b.el.setWidth(width);
33810             
33811             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33812             
33813             b.el.setHeight(height);
33814             
33815         }, this);
33816
33817         var positions = [];
33818         
33819         positions.push({
33820             x : maxX - this.unitWidth * 2 - this.gutter,
33821             y : minY
33822         });
33823         
33824         positions.push({
33825             x : maxX - this.unitWidth,
33826             y : minY + (this.unitWidth + this.gutter) * 2
33827         });
33828         
33829         positions.push({
33830             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33831             y : minY
33832         });
33833         
33834         Roo.each(eItems, function(b,k){
33835             
33836             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33837
33838         }, this);
33839         
33840     },
33841     
33842     getVerticalOneBoxColPositions : function(x, y, box)
33843     {
33844         var pos = [];
33845         
33846         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33847         
33848         if(box[0].size == 'md-left'){
33849             rand = 0;
33850         }
33851         
33852         if(box[0].size == 'md-right'){
33853             rand = 1;
33854         }
33855         
33856         pos.push({
33857             x : x + (this.unitWidth + this.gutter) * rand,
33858             y : y
33859         });
33860         
33861         return pos;
33862     },
33863     
33864     getVerticalTwoBoxColPositions : function(x, y, box)
33865     {
33866         var pos = [];
33867         
33868         if(box[0].size == 'xs'){
33869             
33870             pos.push({
33871                 x : x,
33872                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33873             });
33874
33875             pos.push({
33876                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33877                 y : y
33878             });
33879             
33880             return pos;
33881             
33882         }
33883         
33884         pos.push({
33885             x : x,
33886             y : y
33887         });
33888
33889         pos.push({
33890             x : x + (this.unitWidth + this.gutter) * 2,
33891             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33892         });
33893         
33894         return pos;
33895         
33896     },
33897     
33898     getVerticalThreeBoxColPositions : function(x, y, box)
33899     {
33900         var pos = [];
33901         
33902         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33903             
33904             pos.push({
33905                 x : x,
33906                 y : y
33907             });
33908
33909             pos.push({
33910                 x : x + (this.unitWidth + this.gutter) * 1,
33911                 y : y
33912             });
33913             
33914             pos.push({
33915                 x : x + (this.unitWidth + this.gutter) * 2,
33916                 y : y
33917             });
33918             
33919             return pos;
33920             
33921         }
33922         
33923         if(box[0].size == 'xs' && box[1].size == 'xs'){
33924             
33925             pos.push({
33926                 x : x,
33927                 y : y
33928             });
33929
33930             pos.push({
33931                 x : x,
33932                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33933             });
33934             
33935             pos.push({
33936                 x : x + (this.unitWidth + this.gutter) * 1,
33937                 y : y
33938             });
33939             
33940             return pos;
33941             
33942         }
33943         
33944         pos.push({
33945             x : x,
33946             y : y
33947         });
33948
33949         pos.push({
33950             x : x + (this.unitWidth + this.gutter) * 2,
33951             y : y
33952         });
33953
33954         pos.push({
33955             x : x + (this.unitWidth + this.gutter) * 2,
33956             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33957         });
33958             
33959         return pos;
33960         
33961     },
33962     
33963     getVerticalFourBoxColPositions : function(x, y, box)
33964     {
33965         var pos = [];
33966         
33967         if(box[0].size == 'xs'){
33968             
33969             pos.push({
33970                 x : x,
33971                 y : y
33972             });
33973
33974             pos.push({
33975                 x : x,
33976                 y : y + (this.unitHeight + this.gutter) * 1
33977             });
33978             
33979             pos.push({
33980                 x : x,
33981                 y : y + (this.unitHeight + this.gutter) * 2
33982             });
33983             
33984             pos.push({
33985                 x : x + (this.unitWidth + this.gutter) * 1,
33986                 y : y
33987             });
33988             
33989             return pos;
33990             
33991         }
33992         
33993         pos.push({
33994             x : x,
33995             y : y
33996         });
33997
33998         pos.push({
33999             x : x + (this.unitWidth + this.gutter) * 2,
34000             y : y
34001         });
34002
34003         pos.push({
34004             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34005             y : y + (this.unitHeight + this.gutter) * 1
34006         });
34007
34008         pos.push({
34009             x : x + (this.unitWidth + this.gutter) * 2,
34010             y : y + (this.unitWidth + this.gutter) * 2
34011         });
34012
34013         return pos;
34014         
34015     },
34016     
34017     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34018     {
34019         var pos = [];
34020         
34021         if(box[0].size == 'md-left'){
34022             pos.push({
34023                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34024                 y : minY
34025             });
34026             
34027             return pos;
34028         }
34029         
34030         if(box[0].size == 'md-right'){
34031             pos.push({
34032                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34033                 y : minY + (this.unitWidth + this.gutter) * 1
34034             });
34035             
34036             return pos;
34037         }
34038         
34039         var rand = Math.floor(Math.random() * (4 - box[0].y));
34040         
34041         pos.push({
34042             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34043             y : minY + (this.unitWidth + this.gutter) * rand
34044         });
34045         
34046         return pos;
34047         
34048     },
34049     
34050     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34051     {
34052         var pos = [];
34053         
34054         if(box[0].size == 'xs'){
34055             
34056             pos.push({
34057                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34058                 y : minY
34059             });
34060
34061             pos.push({
34062                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34063                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34064             });
34065             
34066             return pos;
34067             
34068         }
34069         
34070         pos.push({
34071             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34072             y : minY
34073         });
34074
34075         pos.push({
34076             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34077             y : minY + (this.unitWidth + this.gutter) * 2
34078         });
34079         
34080         return pos;
34081         
34082     },
34083     
34084     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34085     {
34086         var pos = [];
34087         
34088         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34089             
34090             pos.push({
34091                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34092                 y : minY
34093             });
34094
34095             pos.push({
34096                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34097                 y : minY + (this.unitWidth + this.gutter) * 1
34098             });
34099             
34100             pos.push({
34101                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34102                 y : minY + (this.unitWidth + this.gutter) * 2
34103             });
34104             
34105             return pos;
34106             
34107         }
34108         
34109         if(box[0].size == 'xs' && box[1].size == 'xs'){
34110             
34111             pos.push({
34112                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34113                 y : minY
34114             });
34115
34116             pos.push({
34117                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34118                 y : minY
34119             });
34120             
34121             pos.push({
34122                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34123                 y : minY + (this.unitWidth + this.gutter) * 1
34124             });
34125             
34126             return pos;
34127             
34128         }
34129         
34130         pos.push({
34131             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34132             y : minY
34133         });
34134
34135         pos.push({
34136             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34137             y : minY + (this.unitWidth + this.gutter) * 2
34138         });
34139
34140         pos.push({
34141             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34142             y : minY + (this.unitWidth + this.gutter) * 2
34143         });
34144             
34145         return pos;
34146         
34147     },
34148     
34149     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34150     {
34151         var pos = [];
34152         
34153         if(box[0].size == 'xs'){
34154             
34155             pos.push({
34156                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34157                 y : minY
34158             });
34159
34160             pos.push({
34161                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34162                 y : minY
34163             });
34164             
34165             pos.push({
34166                 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),
34167                 y : minY
34168             });
34169             
34170             pos.push({
34171                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34172                 y : minY + (this.unitWidth + this.gutter) * 1
34173             });
34174             
34175             return pos;
34176             
34177         }
34178         
34179         pos.push({
34180             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34181             y : minY
34182         });
34183         
34184         pos.push({
34185             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34186             y : minY + (this.unitWidth + this.gutter) * 2
34187         });
34188         
34189         pos.push({
34190             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34191             y : minY + (this.unitWidth + this.gutter) * 2
34192         });
34193         
34194         pos.push({
34195             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),
34196             y : minY + (this.unitWidth + this.gutter) * 2
34197         });
34198
34199         return pos;
34200         
34201     },
34202     
34203     /**
34204     * remove a Masonry Brick
34205     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34206     */
34207     removeBrick : function(brick_id)
34208     {
34209         if (!brick_id) {
34210             return;
34211         }
34212         
34213         for (var i = 0; i<this.bricks.length; i++) {
34214             if (this.bricks[i].id == brick_id) {
34215                 this.bricks.splice(i,1);
34216                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34217                 this.initial();
34218             }
34219         }
34220     },
34221     
34222     /**
34223     * adds a Masonry Brick
34224     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34225     */
34226     addBrick : function(cfg)
34227     {
34228         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34229         //this.register(cn);
34230         cn.parentId = this.id;
34231         cn.render(this.el);
34232         return cn;
34233     },
34234     
34235     /**
34236     * register a Masonry Brick
34237     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34238     */
34239     
34240     register : function(brick)
34241     {
34242         this.bricks.push(brick);
34243         brick.masonryId = this.id;
34244     },
34245     
34246     /**
34247     * clear all the Masonry Brick
34248     */
34249     clearAll : function()
34250     {
34251         this.bricks = [];
34252         //this.getChildContainer().dom.innerHTML = "";
34253         this.el.dom.innerHTML = '';
34254     },
34255     
34256     getSelected : function()
34257     {
34258         if (!this.selectedBrick) {
34259             return false;
34260         }
34261         
34262         return this.selectedBrick;
34263     }
34264 });
34265
34266 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34267     
34268     groups: {},
34269      /**
34270     * register a Masonry Layout
34271     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34272     */
34273     
34274     register : function(layout)
34275     {
34276         this.groups[layout.id] = layout;
34277     },
34278     /**
34279     * fetch a  Masonry Layout based on the masonry layout ID
34280     * @param {string} the masonry layout to add
34281     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34282     */
34283     
34284     get: function(layout_id) {
34285         if (typeof(this.groups[layout_id]) == 'undefined') {
34286             return false;
34287         }
34288         return this.groups[layout_id] ;
34289     }
34290     
34291     
34292     
34293 });
34294
34295  
34296
34297  /**
34298  *
34299  * This is based on 
34300  * http://masonry.desandro.com
34301  *
34302  * The idea is to render all the bricks based on vertical width...
34303  *
34304  * The original code extends 'outlayer' - we might need to use that....
34305  * 
34306  */
34307
34308
34309 /**
34310  * @class Roo.bootstrap.LayoutMasonryAuto
34311  * @extends Roo.bootstrap.Component
34312  * Bootstrap Layout Masonry class
34313  * 
34314  * @constructor
34315  * Create a new Element
34316  * @param {Object} config The config object
34317  */
34318
34319 Roo.bootstrap.LayoutMasonryAuto = function(config){
34320     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34321 };
34322
34323 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34324     
34325       /**
34326      * @cfg {Boolean} isFitWidth  - resize the width..
34327      */   
34328     isFitWidth : false,  // options..
34329     /**
34330      * @cfg {Boolean} isOriginLeft = left align?
34331      */   
34332     isOriginLeft : true,
34333     /**
34334      * @cfg {Boolean} isOriginTop = top align?
34335      */   
34336     isOriginTop : false,
34337     /**
34338      * @cfg {Boolean} isLayoutInstant = no animation?
34339      */   
34340     isLayoutInstant : false, // needed?
34341     /**
34342      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34343      */   
34344     isResizingContainer : true,
34345     /**
34346      * @cfg {Number} columnWidth  width of the columns 
34347      */   
34348     
34349     columnWidth : 0,
34350     
34351     /**
34352      * @cfg {Number} maxCols maximum number of columns
34353      */   
34354     
34355     maxCols: 0,
34356     /**
34357      * @cfg {Number} padHeight padding below box..
34358      */   
34359     
34360     padHeight : 10, 
34361     
34362     /**
34363      * @cfg {Boolean} isAutoInitial defalut true
34364      */   
34365     
34366     isAutoInitial : true, 
34367     
34368     // private?
34369     gutter : 0,
34370     
34371     containerWidth: 0,
34372     initialColumnWidth : 0,
34373     currentSize : null,
34374     
34375     colYs : null, // array.
34376     maxY : 0,
34377     padWidth: 10,
34378     
34379     
34380     tag: 'div',
34381     cls: '',
34382     bricks: null, //CompositeElement
34383     cols : 0, // array?
34384     // element : null, // wrapped now this.el
34385     _isLayoutInited : null, 
34386     
34387     
34388     getAutoCreate : function(){
34389         
34390         var cfg = {
34391             tag: this.tag,
34392             cls: 'blog-masonary-wrapper ' + this.cls,
34393             cn : {
34394                 cls : 'mas-boxes masonary'
34395             }
34396         };
34397         
34398         return cfg;
34399     },
34400     
34401     getChildContainer: function( )
34402     {
34403         if (this.boxesEl) {
34404             return this.boxesEl;
34405         }
34406         
34407         this.boxesEl = this.el.select('.mas-boxes').first();
34408         
34409         return this.boxesEl;
34410     },
34411     
34412     
34413     initEvents : function()
34414     {
34415         var _this = this;
34416         
34417         if(this.isAutoInitial){
34418             Roo.log('hook children rendered');
34419             this.on('childrenrendered', function() {
34420                 Roo.log('children rendered');
34421                 _this.initial();
34422             } ,this);
34423         }
34424         
34425     },
34426     
34427     initial : function()
34428     {
34429         this.reloadItems();
34430
34431         this.currentSize = this.el.getBox(true);
34432
34433         /// was window resize... - let's see if this works..
34434         Roo.EventManager.onWindowResize(this.resize, this); 
34435
34436         if(!this.isAutoInitial){
34437             this.layout();
34438             return;
34439         }
34440         
34441         this.layout.defer(500,this);
34442     },
34443     
34444     reloadItems: function()
34445     {
34446         this.bricks = this.el.select('.masonry-brick', true);
34447         
34448         this.bricks.each(function(b) {
34449             //Roo.log(b.getSize());
34450             if (!b.attr('originalwidth')) {
34451                 b.attr('originalwidth',  b.getSize().width);
34452             }
34453             
34454         });
34455         
34456         Roo.log(this.bricks.elements.length);
34457     },
34458     
34459     resize : function()
34460     {
34461         Roo.log('resize');
34462         var cs = this.el.getBox(true);
34463         
34464         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34465             Roo.log("no change in with or X");
34466             return;
34467         }
34468         this.currentSize = cs;
34469         this.layout();
34470     },
34471     
34472     layout : function()
34473     {
34474          Roo.log('layout');
34475         this._resetLayout();
34476         //this._manageStamps();
34477       
34478         // don't animate first layout
34479         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34480         this.layoutItems( isInstant );
34481       
34482         // flag for initalized
34483         this._isLayoutInited = true;
34484     },
34485     
34486     layoutItems : function( isInstant )
34487     {
34488         //var items = this._getItemsForLayout( this.items );
34489         // original code supports filtering layout items.. we just ignore it..
34490         
34491         this._layoutItems( this.bricks , isInstant );
34492       
34493         this._postLayout();
34494     },
34495     _layoutItems : function ( items , isInstant)
34496     {
34497        //this.fireEvent( 'layout', this, items );
34498     
34499
34500         if ( !items || !items.elements.length ) {
34501           // no items, emit event with empty array
34502             return;
34503         }
34504
34505         var queue = [];
34506         items.each(function(item) {
34507             Roo.log("layout item");
34508             Roo.log(item);
34509             // get x/y object from method
34510             var position = this._getItemLayoutPosition( item );
34511             // enqueue
34512             position.item = item;
34513             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34514             queue.push( position );
34515         }, this);
34516       
34517         this._processLayoutQueue( queue );
34518     },
34519     /** Sets position of item in DOM
34520     * @param {Element} item
34521     * @param {Number} x - horizontal position
34522     * @param {Number} y - vertical position
34523     * @param {Boolean} isInstant - disables transitions
34524     */
34525     _processLayoutQueue : function( queue )
34526     {
34527         for ( var i=0, len = queue.length; i < len; i++ ) {
34528             var obj = queue[i];
34529             obj.item.position('absolute');
34530             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34531         }
34532     },
34533       
34534     
34535     /**
34536     * Any logic you want to do after each layout,
34537     * i.e. size the container
34538     */
34539     _postLayout : function()
34540     {
34541         this.resizeContainer();
34542     },
34543     
34544     resizeContainer : function()
34545     {
34546         if ( !this.isResizingContainer ) {
34547             return;
34548         }
34549         var size = this._getContainerSize();
34550         if ( size ) {
34551             this.el.setSize(size.width,size.height);
34552             this.boxesEl.setSize(size.width,size.height);
34553         }
34554     },
34555     
34556     
34557     
34558     _resetLayout : function()
34559     {
34560         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34561         this.colWidth = this.el.getWidth();
34562         //this.gutter = this.el.getWidth(); 
34563         
34564         this.measureColumns();
34565
34566         // reset column Y
34567         var i = this.cols;
34568         this.colYs = [];
34569         while (i--) {
34570             this.colYs.push( 0 );
34571         }
34572     
34573         this.maxY = 0;
34574     },
34575
34576     measureColumns : function()
34577     {
34578         this.getContainerWidth();
34579       // if columnWidth is 0, default to outerWidth of first item
34580         if ( !this.columnWidth ) {
34581             var firstItem = this.bricks.first();
34582             Roo.log(firstItem);
34583             this.columnWidth  = this.containerWidth;
34584             if (firstItem && firstItem.attr('originalwidth') ) {
34585                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34586             }
34587             // columnWidth fall back to item of first element
34588             Roo.log("set column width?");
34589                         this.initialColumnWidth = this.columnWidth  ;
34590
34591             // if first elem has no width, default to size of container
34592             
34593         }
34594         
34595         
34596         if (this.initialColumnWidth) {
34597             this.columnWidth = this.initialColumnWidth;
34598         }
34599         
34600         
34601             
34602         // column width is fixed at the top - however if container width get's smaller we should
34603         // reduce it...
34604         
34605         // this bit calcs how man columns..
34606             
34607         var columnWidth = this.columnWidth += this.gutter;
34608       
34609         // calculate columns
34610         var containerWidth = this.containerWidth + this.gutter;
34611         
34612         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34613         // fix rounding errors, typically with gutters
34614         var excess = columnWidth - containerWidth % columnWidth;
34615         
34616         
34617         // if overshoot is less than a pixel, round up, otherwise floor it
34618         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34619         cols = Math[ mathMethod ]( cols );
34620         this.cols = Math.max( cols, 1 );
34621         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34622         
34623          // padding positioning..
34624         var totalColWidth = this.cols * this.columnWidth;
34625         var padavail = this.containerWidth - totalColWidth;
34626         // so for 2 columns - we need 3 'pads'
34627         
34628         var padNeeded = (1+this.cols) * this.padWidth;
34629         
34630         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34631         
34632         this.columnWidth += padExtra
34633         //this.padWidth = Math.floor(padavail /  ( this.cols));
34634         
34635         // adjust colum width so that padding is fixed??
34636         
34637         // we have 3 columns ... total = width * 3
34638         // we have X left over... that should be used by 
34639         
34640         //if (this.expandC) {
34641             
34642         //}
34643         
34644         
34645         
34646     },
34647     
34648     getContainerWidth : function()
34649     {
34650        /* // container is parent if fit width
34651         var container = this.isFitWidth ? this.element.parentNode : this.element;
34652         // check that this.size and size are there
34653         // IE8 triggers resize on body size change, so they might not be
34654         
34655         var size = getSize( container );  //FIXME
34656         this.containerWidth = size && size.innerWidth; //FIXME
34657         */
34658          
34659         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34660         
34661     },
34662     
34663     _getItemLayoutPosition : function( item )  // what is item?
34664     {
34665         // we resize the item to our columnWidth..
34666       
34667         item.setWidth(this.columnWidth);
34668         item.autoBoxAdjust  = false;
34669         
34670         var sz = item.getSize();
34671  
34672         // how many columns does this brick span
34673         var remainder = this.containerWidth % this.columnWidth;
34674         
34675         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34676         // round if off by 1 pixel, otherwise use ceil
34677         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34678         colSpan = Math.min( colSpan, this.cols );
34679         
34680         // normally this should be '1' as we dont' currently allow multi width columns..
34681         
34682         var colGroup = this._getColGroup( colSpan );
34683         // get the minimum Y value from the columns
34684         var minimumY = Math.min.apply( Math, colGroup );
34685         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34686         
34687         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34688          
34689         // position the brick
34690         var position = {
34691             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34692             y: this.currentSize.y + minimumY + this.padHeight
34693         };
34694         
34695         Roo.log(position);
34696         // apply setHeight to necessary columns
34697         var setHeight = minimumY + sz.height + this.padHeight;
34698         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34699         
34700         var setSpan = this.cols + 1 - colGroup.length;
34701         for ( var i = 0; i < setSpan; i++ ) {
34702           this.colYs[ shortColIndex + i ] = setHeight ;
34703         }
34704       
34705         return position;
34706     },
34707     
34708     /**
34709      * @param {Number} colSpan - number of columns the element spans
34710      * @returns {Array} colGroup
34711      */
34712     _getColGroup : function( colSpan )
34713     {
34714         if ( colSpan < 2 ) {
34715           // if brick spans only one column, use all the column Ys
34716           return this.colYs;
34717         }
34718       
34719         var colGroup = [];
34720         // how many different places could this brick fit horizontally
34721         var groupCount = this.cols + 1 - colSpan;
34722         // for each group potential horizontal position
34723         for ( var i = 0; i < groupCount; i++ ) {
34724           // make an array of colY values for that one group
34725           var groupColYs = this.colYs.slice( i, i + colSpan );
34726           // and get the max value of the array
34727           colGroup[i] = Math.max.apply( Math, groupColYs );
34728         }
34729         return colGroup;
34730     },
34731     /*
34732     _manageStamp : function( stamp )
34733     {
34734         var stampSize =  stamp.getSize();
34735         var offset = stamp.getBox();
34736         // get the columns that this stamp affects
34737         var firstX = this.isOriginLeft ? offset.x : offset.right;
34738         var lastX = firstX + stampSize.width;
34739         var firstCol = Math.floor( firstX / this.columnWidth );
34740         firstCol = Math.max( 0, firstCol );
34741         
34742         var lastCol = Math.floor( lastX / this.columnWidth );
34743         // lastCol should not go over if multiple of columnWidth #425
34744         lastCol -= lastX % this.columnWidth ? 0 : 1;
34745         lastCol = Math.min( this.cols - 1, lastCol );
34746         
34747         // set colYs to bottom of the stamp
34748         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34749             stampSize.height;
34750             
34751         for ( var i = firstCol; i <= lastCol; i++ ) {
34752           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34753         }
34754     },
34755     */
34756     
34757     _getContainerSize : function()
34758     {
34759         this.maxY = Math.max.apply( Math, this.colYs );
34760         var size = {
34761             height: this.maxY
34762         };
34763       
34764         if ( this.isFitWidth ) {
34765             size.width = this._getContainerFitWidth();
34766         }
34767       
34768         return size;
34769     },
34770     
34771     _getContainerFitWidth : function()
34772     {
34773         var unusedCols = 0;
34774         // count unused columns
34775         var i = this.cols;
34776         while ( --i ) {
34777           if ( this.colYs[i] !== 0 ) {
34778             break;
34779           }
34780           unusedCols++;
34781         }
34782         // fit container to columns that have been used
34783         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34784     },
34785     
34786     needsResizeLayout : function()
34787     {
34788         var previousWidth = this.containerWidth;
34789         this.getContainerWidth();
34790         return previousWidth !== this.containerWidth;
34791     }
34792  
34793 });
34794
34795  
34796
34797  /*
34798  * - LGPL
34799  *
34800  * element
34801  * 
34802  */
34803
34804 /**
34805  * @class Roo.bootstrap.MasonryBrick
34806  * @extends Roo.bootstrap.Component
34807  * Bootstrap MasonryBrick class
34808  * 
34809  * @constructor
34810  * Create a new MasonryBrick
34811  * @param {Object} config The config object
34812  */
34813
34814 Roo.bootstrap.MasonryBrick = function(config){
34815     
34816     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34817     
34818     Roo.bootstrap.MasonryBrick.register(this);
34819     
34820     this.addEvents({
34821         // raw events
34822         /**
34823          * @event click
34824          * When a MasonryBrick is clcik
34825          * @param {Roo.bootstrap.MasonryBrick} this
34826          * @param {Roo.EventObject} e
34827          */
34828         "click" : true
34829     });
34830 };
34831
34832 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34833     
34834     /**
34835      * @cfg {String} title
34836      */   
34837     title : '',
34838     /**
34839      * @cfg {String} html
34840      */   
34841     html : '',
34842     /**
34843      * @cfg {String} bgimage
34844      */   
34845     bgimage : '',
34846     /**
34847      * @cfg {String} videourl
34848      */   
34849     videourl : '',
34850     /**
34851      * @cfg {String} cls
34852      */   
34853     cls : '',
34854     /**
34855      * @cfg {String} href
34856      */   
34857     href : '',
34858     /**
34859      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34860      */   
34861     size : 'xs',
34862     
34863     /**
34864      * @cfg {String} placetitle (center|bottom)
34865      */   
34866     placetitle : '',
34867     
34868     /**
34869      * @cfg {Boolean} isFitContainer defalut true
34870      */   
34871     isFitContainer : true, 
34872     
34873     /**
34874      * @cfg {Boolean} preventDefault defalut false
34875      */   
34876     preventDefault : false, 
34877     
34878     /**
34879      * @cfg {Boolean} inverse defalut false
34880      */   
34881     maskInverse : false, 
34882     
34883     getAutoCreate : function()
34884     {
34885         if(!this.isFitContainer){
34886             return this.getSplitAutoCreate();
34887         }
34888         
34889         var cls = 'masonry-brick masonry-brick-full';
34890         
34891         if(this.href.length){
34892             cls += ' masonry-brick-link';
34893         }
34894         
34895         if(this.bgimage.length){
34896             cls += ' masonry-brick-image';
34897         }
34898         
34899         if(this.maskInverse){
34900             cls += ' mask-inverse';
34901         }
34902         
34903         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34904             cls += ' enable-mask';
34905         }
34906         
34907         if(this.size){
34908             cls += ' masonry-' + this.size + '-brick';
34909         }
34910         
34911         if(this.placetitle.length){
34912             
34913             switch (this.placetitle) {
34914                 case 'center' :
34915                     cls += ' masonry-center-title';
34916                     break;
34917                 case 'bottom' :
34918                     cls += ' masonry-bottom-title';
34919                     break;
34920                 default:
34921                     break;
34922             }
34923             
34924         } else {
34925             if(!this.html.length && !this.bgimage.length){
34926                 cls += ' masonry-center-title';
34927             }
34928
34929             if(!this.html.length && this.bgimage.length){
34930                 cls += ' masonry-bottom-title';
34931             }
34932         }
34933         
34934         if(this.cls){
34935             cls += ' ' + this.cls;
34936         }
34937         
34938         var cfg = {
34939             tag: (this.href.length) ? 'a' : 'div',
34940             cls: cls,
34941             cn: [
34942                 {
34943                     tag: 'div',
34944                     cls: 'masonry-brick-mask'
34945                 },
34946                 {
34947                     tag: 'div',
34948                     cls: 'masonry-brick-paragraph',
34949                     cn: []
34950                 }
34951             ]
34952         };
34953         
34954         if(this.href.length){
34955             cfg.href = this.href;
34956         }
34957         
34958         var cn = cfg.cn[1].cn;
34959         
34960         if(this.title.length){
34961             cn.push({
34962                 tag: 'h4',
34963                 cls: 'masonry-brick-title',
34964                 html: this.title
34965             });
34966         }
34967         
34968         if(this.html.length){
34969             cn.push({
34970                 tag: 'p',
34971                 cls: 'masonry-brick-text',
34972                 html: this.html
34973             });
34974         }
34975         
34976         if (!this.title.length && !this.html.length) {
34977             cfg.cn[1].cls += ' hide';
34978         }
34979         
34980         if(this.bgimage.length){
34981             cfg.cn.push({
34982                 tag: 'img',
34983                 cls: 'masonry-brick-image-view',
34984                 src: this.bgimage
34985             });
34986         }
34987         
34988         if(this.videourl.length){
34989             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34990             // youtube support only?
34991             cfg.cn.push({
34992                 tag: 'iframe',
34993                 cls: 'masonry-brick-image-view',
34994                 src: vurl,
34995                 frameborder : 0,
34996                 allowfullscreen : true
34997             });
34998         }
34999         
35000         return cfg;
35001         
35002     },
35003     
35004     getSplitAutoCreate : function()
35005     {
35006         var cls = 'masonry-brick masonry-brick-split';
35007         
35008         if(this.href.length){
35009             cls += ' masonry-brick-link';
35010         }
35011         
35012         if(this.bgimage.length){
35013             cls += ' masonry-brick-image';
35014         }
35015         
35016         if(this.size){
35017             cls += ' masonry-' + this.size + '-brick';
35018         }
35019         
35020         switch (this.placetitle) {
35021             case 'center' :
35022                 cls += ' masonry-center-title';
35023                 break;
35024             case 'bottom' :
35025                 cls += ' masonry-bottom-title';
35026                 break;
35027             default:
35028                 if(!this.bgimage.length){
35029                     cls += ' masonry-center-title';
35030                 }
35031
35032                 if(this.bgimage.length){
35033                     cls += ' masonry-bottom-title';
35034                 }
35035                 break;
35036         }
35037         
35038         if(this.cls){
35039             cls += ' ' + this.cls;
35040         }
35041         
35042         var cfg = {
35043             tag: (this.href.length) ? 'a' : 'div',
35044             cls: cls,
35045             cn: [
35046                 {
35047                     tag: 'div',
35048                     cls: 'masonry-brick-split-head',
35049                     cn: [
35050                         {
35051                             tag: 'div',
35052                             cls: 'masonry-brick-paragraph',
35053                             cn: []
35054                         }
35055                     ]
35056                 },
35057                 {
35058                     tag: 'div',
35059                     cls: 'masonry-brick-split-body',
35060                     cn: []
35061                 }
35062             ]
35063         };
35064         
35065         if(this.href.length){
35066             cfg.href = this.href;
35067         }
35068         
35069         if(this.title.length){
35070             cfg.cn[0].cn[0].cn.push({
35071                 tag: 'h4',
35072                 cls: 'masonry-brick-title',
35073                 html: this.title
35074             });
35075         }
35076         
35077         if(this.html.length){
35078             cfg.cn[1].cn.push({
35079                 tag: 'p',
35080                 cls: 'masonry-brick-text',
35081                 html: this.html
35082             });
35083         }
35084
35085         if(this.bgimage.length){
35086             cfg.cn[0].cn.push({
35087                 tag: 'img',
35088                 cls: 'masonry-brick-image-view',
35089                 src: this.bgimage
35090             });
35091         }
35092         
35093         if(this.videourl.length){
35094             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35095             // youtube support only?
35096             cfg.cn[0].cn.cn.push({
35097                 tag: 'iframe',
35098                 cls: 'masonry-brick-image-view',
35099                 src: vurl,
35100                 frameborder : 0,
35101                 allowfullscreen : true
35102             });
35103         }
35104         
35105         return cfg;
35106     },
35107     
35108     initEvents: function() 
35109     {
35110         switch (this.size) {
35111             case 'xs' :
35112                 this.x = 1;
35113                 this.y = 1;
35114                 break;
35115             case 'sm' :
35116                 this.x = 2;
35117                 this.y = 2;
35118                 break;
35119             case 'md' :
35120             case 'md-left' :
35121             case 'md-right' :
35122                 this.x = 3;
35123                 this.y = 3;
35124                 break;
35125             case 'tall' :
35126                 this.x = 2;
35127                 this.y = 3;
35128                 break;
35129             case 'wide' :
35130                 this.x = 3;
35131                 this.y = 2;
35132                 break;
35133             case 'wide-thin' :
35134                 this.x = 3;
35135                 this.y = 1;
35136                 break;
35137                         
35138             default :
35139                 break;
35140         }
35141         
35142         if(Roo.isTouch){
35143             this.el.on('touchstart', this.onTouchStart, this);
35144             this.el.on('touchmove', this.onTouchMove, this);
35145             this.el.on('touchend', this.onTouchEnd, this);
35146             this.el.on('contextmenu', this.onContextMenu, this);
35147         } else {
35148             this.el.on('mouseenter'  ,this.enter, this);
35149             this.el.on('mouseleave', this.leave, this);
35150             this.el.on('click', this.onClick, this);
35151         }
35152         
35153         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35154             this.parent().bricks.push(this);   
35155         }
35156         
35157     },
35158     
35159     onClick: function(e, el)
35160     {
35161         var time = this.endTimer - this.startTimer;
35162         // Roo.log(e.preventDefault());
35163         if(Roo.isTouch){
35164             if(time > 1000){
35165                 e.preventDefault();
35166                 return;
35167             }
35168         }
35169         
35170         if(!this.preventDefault){
35171             return;
35172         }
35173         
35174         e.preventDefault();
35175         
35176         if (this.activeClass != '') {
35177             this.selectBrick();
35178         }
35179         
35180         this.fireEvent('click', this, e);
35181     },
35182     
35183     enter: function(e, el)
35184     {
35185         e.preventDefault();
35186         
35187         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35188             return;
35189         }
35190         
35191         if(this.bgimage.length && this.html.length){
35192             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35193         }
35194     },
35195     
35196     leave: function(e, el)
35197     {
35198         e.preventDefault();
35199         
35200         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35201             return;
35202         }
35203         
35204         if(this.bgimage.length && this.html.length){
35205             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35206         }
35207     },
35208     
35209     onTouchStart: function(e, el)
35210     {
35211 //        e.preventDefault();
35212         
35213         this.touchmoved = false;
35214         
35215         if(!this.isFitContainer){
35216             return;
35217         }
35218         
35219         if(!this.bgimage.length || !this.html.length){
35220             return;
35221         }
35222         
35223         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35224         
35225         this.timer = new Date().getTime();
35226         
35227     },
35228     
35229     onTouchMove: function(e, el)
35230     {
35231         this.touchmoved = true;
35232     },
35233     
35234     onContextMenu : function(e,el)
35235     {
35236         e.preventDefault();
35237         e.stopPropagation();
35238         return false;
35239     },
35240     
35241     onTouchEnd: function(e, el)
35242     {
35243 //        e.preventDefault();
35244         
35245         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35246         
35247             this.leave(e,el);
35248             
35249             return;
35250         }
35251         
35252         if(!this.bgimage.length || !this.html.length){
35253             
35254             if(this.href.length){
35255                 window.location.href = this.href;
35256             }
35257             
35258             return;
35259         }
35260         
35261         if(!this.isFitContainer){
35262             return;
35263         }
35264         
35265         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35266         
35267         window.location.href = this.href;
35268     },
35269     
35270     //selection on single brick only
35271     selectBrick : function() {
35272         
35273         if (!this.parentId) {
35274             return;
35275         }
35276         
35277         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35278         var index = m.selectedBrick.indexOf(this.id);
35279         
35280         if ( index > -1) {
35281             m.selectedBrick.splice(index,1);
35282             this.el.removeClass(this.activeClass);
35283             return;
35284         }
35285         
35286         for(var i = 0; i < m.selectedBrick.length; i++) {
35287             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35288             b.el.removeClass(b.activeClass);
35289         }
35290         
35291         m.selectedBrick = [];
35292         
35293         m.selectedBrick.push(this.id);
35294         this.el.addClass(this.activeClass);
35295         return;
35296     },
35297     
35298     isSelected : function(){
35299         return this.el.hasClass(this.activeClass);
35300         
35301     }
35302 });
35303
35304 Roo.apply(Roo.bootstrap.MasonryBrick, {
35305     
35306     //groups: {},
35307     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35308      /**
35309     * register a Masonry Brick
35310     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35311     */
35312     
35313     register : function(brick)
35314     {
35315         //this.groups[brick.id] = brick;
35316         this.groups.add(brick.id, brick);
35317     },
35318     /**
35319     * fetch a  masonry brick based on the masonry brick ID
35320     * @param {string} the masonry brick to add
35321     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35322     */
35323     
35324     get: function(brick_id) 
35325     {
35326         // if (typeof(this.groups[brick_id]) == 'undefined') {
35327         //     return false;
35328         // }
35329         // return this.groups[brick_id] ;
35330         
35331         if(this.groups.key(brick_id)) {
35332             return this.groups.key(brick_id);
35333         }
35334         
35335         return false;
35336     }
35337     
35338     
35339     
35340 });
35341
35342  /*
35343  * - LGPL
35344  *
35345  * element
35346  * 
35347  */
35348
35349 /**
35350  * @class Roo.bootstrap.Brick
35351  * @extends Roo.bootstrap.Component
35352  * Bootstrap Brick class
35353  * 
35354  * @constructor
35355  * Create a new Brick
35356  * @param {Object} config The config object
35357  */
35358
35359 Roo.bootstrap.Brick = function(config){
35360     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35361     
35362     this.addEvents({
35363         // raw events
35364         /**
35365          * @event click
35366          * When a Brick is click
35367          * @param {Roo.bootstrap.Brick} this
35368          * @param {Roo.EventObject} e
35369          */
35370         "click" : true
35371     });
35372 };
35373
35374 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35375     
35376     /**
35377      * @cfg {String} title
35378      */   
35379     title : '',
35380     /**
35381      * @cfg {String} html
35382      */   
35383     html : '',
35384     /**
35385      * @cfg {String} bgimage
35386      */   
35387     bgimage : '',
35388     /**
35389      * @cfg {String} cls
35390      */   
35391     cls : '',
35392     /**
35393      * @cfg {String} href
35394      */   
35395     href : '',
35396     /**
35397      * @cfg {String} video
35398      */   
35399     video : '',
35400     /**
35401      * @cfg {Boolean} square
35402      */   
35403     square : true,
35404     
35405     getAutoCreate : function()
35406     {
35407         var cls = 'roo-brick';
35408         
35409         if(this.href.length){
35410             cls += ' roo-brick-link';
35411         }
35412         
35413         if(this.bgimage.length){
35414             cls += ' roo-brick-image';
35415         }
35416         
35417         if(!this.html.length && !this.bgimage.length){
35418             cls += ' roo-brick-center-title';
35419         }
35420         
35421         if(!this.html.length && this.bgimage.length){
35422             cls += ' roo-brick-bottom-title';
35423         }
35424         
35425         if(this.cls){
35426             cls += ' ' + this.cls;
35427         }
35428         
35429         var cfg = {
35430             tag: (this.href.length) ? 'a' : 'div',
35431             cls: cls,
35432             cn: [
35433                 {
35434                     tag: 'div',
35435                     cls: 'roo-brick-paragraph',
35436                     cn: []
35437                 }
35438             ]
35439         };
35440         
35441         if(this.href.length){
35442             cfg.href = this.href;
35443         }
35444         
35445         var cn = cfg.cn[0].cn;
35446         
35447         if(this.title.length){
35448             cn.push({
35449                 tag: 'h4',
35450                 cls: 'roo-brick-title',
35451                 html: this.title
35452             });
35453         }
35454         
35455         if(this.html.length){
35456             cn.push({
35457                 tag: 'p',
35458                 cls: 'roo-brick-text',
35459                 html: this.html
35460             });
35461         } else {
35462             cn.cls += ' hide';
35463         }
35464         
35465         if(this.bgimage.length){
35466             cfg.cn.push({
35467                 tag: 'img',
35468                 cls: 'roo-brick-image-view',
35469                 src: this.bgimage
35470             });
35471         }
35472         
35473         return cfg;
35474     },
35475     
35476     initEvents: function() 
35477     {
35478         if(this.title.length || this.html.length){
35479             this.el.on('mouseenter'  ,this.enter, this);
35480             this.el.on('mouseleave', this.leave, this);
35481         }
35482         
35483         Roo.EventManager.onWindowResize(this.resize, this); 
35484         
35485         if(this.bgimage.length){
35486             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35487             this.imageEl.on('load', this.onImageLoad, this);
35488             return;
35489         }
35490         
35491         this.resize();
35492     },
35493     
35494     onImageLoad : function()
35495     {
35496         this.resize();
35497     },
35498     
35499     resize : function()
35500     {
35501         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35502         
35503         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35504         
35505         if(this.bgimage.length){
35506             var image = this.el.select('.roo-brick-image-view', true).first();
35507             
35508             image.setWidth(paragraph.getWidth());
35509             
35510             if(this.square){
35511                 image.setHeight(paragraph.getWidth());
35512             }
35513             
35514             this.el.setHeight(image.getHeight());
35515             paragraph.setHeight(image.getHeight());
35516             
35517         }
35518         
35519     },
35520     
35521     enter: function(e, el)
35522     {
35523         e.preventDefault();
35524         
35525         if(this.bgimage.length){
35526             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35527             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35528         }
35529     },
35530     
35531     leave: function(e, el)
35532     {
35533         e.preventDefault();
35534         
35535         if(this.bgimage.length){
35536             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35537             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35538         }
35539     }
35540     
35541 });
35542
35543  
35544
35545  /*
35546  * - LGPL
35547  *
35548  * Number field 
35549  */
35550
35551 /**
35552  * @class Roo.bootstrap.NumberField
35553  * @extends Roo.bootstrap.Input
35554  * Bootstrap NumberField class
35555  * 
35556  * 
35557  * 
35558  * 
35559  * @constructor
35560  * Create a new NumberField
35561  * @param {Object} config The config object
35562  */
35563
35564 Roo.bootstrap.NumberField = function(config){
35565     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35566 };
35567
35568 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35569     
35570     /**
35571      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35572      */
35573     allowDecimals : true,
35574     /**
35575      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35576      */
35577     decimalSeparator : ".",
35578     /**
35579      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35580      */
35581     decimalPrecision : 2,
35582     /**
35583      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35584      */
35585     allowNegative : true,
35586     
35587     /**
35588      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35589      */
35590     allowZero: true,
35591     /**
35592      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35593      */
35594     minValue : Number.NEGATIVE_INFINITY,
35595     /**
35596      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35597      */
35598     maxValue : Number.MAX_VALUE,
35599     /**
35600      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35601      */
35602     minText : "The minimum value for this field is {0}",
35603     /**
35604      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35605      */
35606     maxText : "The maximum value for this field is {0}",
35607     /**
35608      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35609      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35610      */
35611     nanText : "{0} is not a valid number",
35612     /**
35613      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35614      */
35615     thousandsDelimiter : false,
35616     /**
35617      * @cfg {String} valueAlign alignment of value
35618      */
35619     valueAlign : "left",
35620
35621     getAutoCreate : function()
35622     {
35623         var hiddenInput = {
35624             tag: 'input',
35625             type: 'hidden',
35626             id: Roo.id(),
35627             cls: 'hidden-number-input'
35628         };
35629         
35630         if (this.name) {
35631             hiddenInput.name = this.name;
35632         }
35633         
35634         this.name = '';
35635         
35636         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35637         
35638         this.name = hiddenInput.name;
35639         
35640         if(cfg.cn.length > 0) {
35641             cfg.cn.push(hiddenInput);
35642         }
35643         
35644         return cfg;
35645     },
35646
35647     // private
35648     initEvents : function()
35649     {   
35650         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35651         
35652         var allowed = "0123456789";
35653         
35654         if(this.allowDecimals){
35655             allowed += this.decimalSeparator;
35656         }
35657         
35658         if(this.allowNegative){
35659             allowed += "-";
35660         }
35661         
35662         if(this.thousandsDelimiter) {
35663             allowed += ",";
35664         }
35665         
35666         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35667         
35668         var keyPress = function(e){
35669             
35670             var k = e.getKey();
35671             
35672             var c = e.getCharCode();
35673             
35674             if(
35675                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35676                     allowed.indexOf(String.fromCharCode(c)) === -1
35677             ){
35678                 e.stopEvent();
35679                 return;
35680             }
35681             
35682             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35683                 return;
35684             }
35685             
35686             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35687                 e.stopEvent();
35688             }
35689         };
35690         
35691         this.el.on("keypress", keyPress, this);
35692     },
35693     
35694     validateValue : function(value)
35695     {
35696         
35697         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35698             return false;
35699         }
35700         
35701         var num = this.parseValue(value);
35702         
35703         if(isNaN(num)){
35704             this.markInvalid(String.format(this.nanText, value));
35705             return false;
35706         }
35707         
35708         if(num < this.minValue){
35709             this.markInvalid(String.format(this.minText, this.minValue));
35710             return false;
35711         }
35712         
35713         if(num > this.maxValue){
35714             this.markInvalid(String.format(this.maxText, this.maxValue));
35715             return false;
35716         }
35717         
35718         return true;
35719     },
35720
35721     getValue : function()
35722     {
35723         var v = this.hiddenEl().getValue();
35724         
35725         return this.fixPrecision(this.parseValue(v));
35726     },
35727
35728     parseValue : function(value)
35729     {
35730         if(this.thousandsDelimiter) {
35731             value += "";
35732             r = new RegExp(",", "g");
35733             value = value.replace(r, "");
35734         }
35735         
35736         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35737         return isNaN(value) ? '' : value;
35738     },
35739
35740     fixPrecision : function(value)
35741     {
35742         if(this.thousandsDelimiter) {
35743             value += "";
35744             r = new RegExp(",", "g");
35745             value = value.replace(r, "");
35746         }
35747         
35748         var nan = isNaN(value);
35749         
35750         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35751             return nan ? '' : value;
35752         }
35753         return parseFloat(value).toFixed(this.decimalPrecision);
35754     },
35755
35756     setValue : function(v)
35757     {
35758         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35759         
35760         this.value = v;
35761         
35762         if(this.rendered){
35763             
35764             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35765             
35766             this.inputEl().dom.value = (v == '') ? '' :
35767                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35768             
35769             if(!this.allowZero && v === '0') {
35770                 this.hiddenEl().dom.value = '';
35771                 this.inputEl().dom.value = '';
35772             }
35773             
35774             this.validate();
35775         }
35776     },
35777
35778     decimalPrecisionFcn : function(v)
35779     {
35780         return Math.floor(v);
35781     },
35782
35783     beforeBlur : function()
35784     {
35785         var v = this.parseValue(this.getRawValue());
35786         
35787         if(v || v === 0 || v === ''){
35788             this.setValue(v);
35789         }
35790     },
35791     
35792     hiddenEl : function()
35793     {
35794         return this.el.select('input.hidden-number-input',true).first();
35795     }
35796     
35797 });
35798
35799  
35800
35801 /*
35802 * Licence: LGPL
35803 */
35804
35805 /**
35806  * @class Roo.bootstrap.DocumentSlider
35807  * @extends Roo.bootstrap.Component
35808  * Bootstrap DocumentSlider class
35809  * 
35810  * @constructor
35811  * Create a new DocumentViewer
35812  * @param {Object} config The config object
35813  */
35814
35815 Roo.bootstrap.DocumentSlider = function(config){
35816     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35817     
35818     this.files = [];
35819     
35820     this.addEvents({
35821         /**
35822          * @event initial
35823          * Fire after initEvent
35824          * @param {Roo.bootstrap.DocumentSlider} this
35825          */
35826         "initial" : true,
35827         /**
35828          * @event update
35829          * Fire after update
35830          * @param {Roo.bootstrap.DocumentSlider} this
35831          */
35832         "update" : true,
35833         /**
35834          * @event click
35835          * Fire after click
35836          * @param {Roo.bootstrap.DocumentSlider} this
35837          */
35838         "click" : true
35839     });
35840 };
35841
35842 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35843     
35844     files : false,
35845     
35846     indicator : 0,
35847     
35848     getAutoCreate : function()
35849     {
35850         var cfg = {
35851             tag : 'div',
35852             cls : 'roo-document-slider',
35853             cn : [
35854                 {
35855                     tag : 'div',
35856                     cls : 'roo-document-slider-header',
35857                     cn : [
35858                         {
35859                             tag : 'div',
35860                             cls : 'roo-document-slider-header-title'
35861                         }
35862                     ]
35863                 },
35864                 {
35865                     tag : 'div',
35866                     cls : 'roo-document-slider-body',
35867                     cn : [
35868                         {
35869                             tag : 'div',
35870                             cls : 'roo-document-slider-prev',
35871                             cn : [
35872                                 {
35873                                     tag : 'i',
35874                                     cls : 'fa fa-chevron-left'
35875                                 }
35876                             ]
35877                         },
35878                         {
35879                             tag : 'div',
35880                             cls : 'roo-document-slider-thumb',
35881                             cn : [
35882                                 {
35883                                     tag : 'img',
35884                                     cls : 'roo-document-slider-image'
35885                                 }
35886                             ]
35887                         },
35888                         {
35889                             tag : 'div',
35890                             cls : 'roo-document-slider-next',
35891                             cn : [
35892                                 {
35893                                     tag : 'i',
35894                                     cls : 'fa fa-chevron-right'
35895                                 }
35896                             ]
35897                         }
35898                     ]
35899                 }
35900             ]
35901         };
35902         
35903         return cfg;
35904     },
35905     
35906     initEvents : function()
35907     {
35908         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35909         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35910         
35911         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35912         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35913         
35914         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35915         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35916         
35917         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35918         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35919         
35920         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35921         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35922         
35923         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35924         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35925         
35926         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35927         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35928         
35929         this.thumbEl.on('click', this.onClick, this);
35930         
35931         this.prevIndicator.on('click', this.prev, this);
35932         
35933         this.nextIndicator.on('click', this.next, this);
35934         
35935     },
35936     
35937     initial : function()
35938     {
35939         if(this.files.length){
35940             this.indicator = 1;
35941             this.update()
35942         }
35943         
35944         this.fireEvent('initial', this);
35945     },
35946     
35947     update : function()
35948     {
35949         this.imageEl.attr('src', this.files[this.indicator - 1]);
35950         
35951         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35952         
35953         this.prevIndicator.show();
35954         
35955         if(this.indicator == 1){
35956             this.prevIndicator.hide();
35957         }
35958         
35959         this.nextIndicator.show();
35960         
35961         if(this.indicator == this.files.length){
35962             this.nextIndicator.hide();
35963         }
35964         
35965         this.thumbEl.scrollTo('top');
35966         
35967         this.fireEvent('update', this);
35968     },
35969     
35970     onClick : function(e)
35971     {
35972         e.preventDefault();
35973         
35974         this.fireEvent('click', this);
35975     },
35976     
35977     prev : function(e)
35978     {
35979         e.preventDefault();
35980         
35981         this.indicator = Math.max(1, this.indicator - 1);
35982         
35983         this.update();
35984     },
35985     
35986     next : function(e)
35987     {
35988         e.preventDefault();
35989         
35990         this.indicator = Math.min(this.files.length, this.indicator + 1);
35991         
35992         this.update();
35993     }
35994 });
35995 /*
35996  * - LGPL
35997  *
35998  * RadioSet
35999  *
36000  *
36001  */
36002
36003 /**
36004  * @class Roo.bootstrap.RadioSet
36005  * @extends Roo.bootstrap.Input
36006  * Bootstrap RadioSet class
36007  * @cfg {String} indicatorpos (left|right) default left
36008  * @cfg {Boolean} inline (true|false) inline the element (default true)
36009  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36010  * @constructor
36011  * Create a new RadioSet
36012  * @param {Object} config The config object
36013  */
36014
36015 Roo.bootstrap.RadioSet = function(config){
36016     
36017     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36018     
36019     this.radioes = [];
36020     
36021     Roo.bootstrap.RadioSet.register(this);
36022     
36023     this.addEvents({
36024         /**
36025         * @event check
36026         * Fires when the element is checked or unchecked.
36027         * @param {Roo.bootstrap.RadioSet} this This radio
36028         * @param {Roo.bootstrap.Radio} item The checked item
36029         */
36030        check : true,
36031        /**
36032         * @event click
36033         * Fires when the element is click.
36034         * @param {Roo.bootstrap.RadioSet} this This radio set
36035         * @param {Roo.bootstrap.Radio} item The checked item
36036         * @param {Roo.EventObject} e The event object
36037         */
36038        click : true
36039     });
36040     
36041 };
36042
36043 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36044
36045     radioes : false,
36046     
36047     inline : true,
36048     
36049     weight : '',
36050     
36051     indicatorpos : 'left',
36052     
36053     getAutoCreate : function()
36054     {
36055         var label = {
36056             tag : 'label',
36057             cls : 'roo-radio-set-label',
36058             cn : [
36059                 {
36060                     tag : 'span',
36061                     html : this.fieldLabel
36062                 }
36063             ]
36064         };
36065         if (Roo.bootstrap.version == 3) {
36066             
36067             
36068             if(this.indicatorpos == 'left'){
36069                 label.cn.unshift({
36070                     tag : 'i',
36071                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36072                     tooltip : 'This field is required'
36073                 });
36074             } else {
36075                 label.cn.push({
36076                     tag : 'i',
36077                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36078                     tooltip : 'This field is required'
36079                 });
36080             }
36081         }
36082         var items = {
36083             tag : 'div',
36084             cls : 'roo-radio-set-items'
36085         };
36086         
36087         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36088         
36089         if (align === 'left' && this.fieldLabel.length) {
36090             
36091             items = {
36092                 cls : "roo-radio-set-right", 
36093                 cn: [
36094                     items
36095                 ]
36096             };
36097             
36098             if(this.labelWidth > 12){
36099                 label.style = "width: " + this.labelWidth + 'px';
36100             }
36101             
36102             if(this.labelWidth < 13 && this.labelmd == 0){
36103                 this.labelmd = this.labelWidth;
36104             }
36105             
36106             if(this.labellg > 0){
36107                 label.cls += ' col-lg-' + this.labellg;
36108                 items.cls += ' col-lg-' + (12 - this.labellg);
36109             }
36110             
36111             if(this.labelmd > 0){
36112                 label.cls += ' col-md-' + this.labelmd;
36113                 items.cls += ' col-md-' + (12 - this.labelmd);
36114             }
36115             
36116             if(this.labelsm > 0){
36117                 label.cls += ' col-sm-' + this.labelsm;
36118                 items.cls += ' col-sm-' + (12 - this.labelsm);
36119             }
36120             
36121             if(this.labelxs > 0){
36122                 label.cls += ' col-xs-' + this.labelxs;
36123                 items.cls += ' col-xs-' + (12 - this.labelxs);
36124             }
36125         }
36126         
36127         var cfg = {
36128             tag : 'div',
36129             cls : 'roo-radio-set',
36130             cn : [
36131                 {
36132                     tag : 'input',
36133                     cls : 'roo-radio-set-input',
36134                     type : 'hidden',
36135                     name : this.name,
36136                     value : this.value ? this.value :  ''
36137                 },
36138                 label,
36139                 items
36140             ]
36141         };
36142         
36143         if(this.weight.length){
36144             cfg.cls += ' roo-radio-' + this.weight;
36145         }
36146         
36147         if(this.inline) {
36148             cfg.cls += ' roo-radio-set-inline';
36149         }
36150         
36151         var settings=this;
36152         ['xs','sm','md','lg'].map(function(size){
36153             if (settings[size]) {
36154                 cfg.cls += ' col-' + size + '-' + settings[size];
36155             }
36156         });
36157         
36158         return cfg;
36159         
36160     },
36161
36162     initEvents : function()
36163     {
36164         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36165         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36166         
36167         if(!this.fieldLabel.length){
36168             this.labelEl.hide();
36169         }
36170         
36171         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36172         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36173         
36174         this.indicator = this.indicatorEl();
36175         
36176         if(this.indicator){
36177             this.indicator.addClass('invisible');
36178         }
36179         
36180         this.originalValue = this.getValue();
36181         
36182     },
36183     
36184     inputEl: function ()
36185     {
36186         return this.el.select('.roo-radio-set-input', true).first();
36187     },
36188     
36189     getChildContainer : function()
36190     {
36191         return this.itemsEl;
36192     },
36193     
36194     register : function(item)
36195     {
36196         this.radioes.push(item);
36197         
36198     },
36199     
36200     validate : function()
36201     {   
36202         if(this.getVisibilityEl().hasClass('hidden')){
36203             return true;
36204         }
36205         
36206         var valid = false;
36207         
36208         Roo.each(this.radioes, function(i){
36209             if(!i.checked){
36210                 return;
36211             }
36212             
36213             valid = true;
36214             return false;
36215         });
36216         
36217         if(this.allowBlank) {
36218             return true;
36219         }
36220         
36221         if(this.disabled || valid){
36222             this.markValid();
36223             return true;
36224         }
36225         
36226         this.markInvalid();
36227         return false;
36228         
36229     },
36230     
36231     markValid : function()
36232     {
36233         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36234             this.indicatorEl().removeClass('visible');
36235             this.indicatorEl().addClass('invisible');
36236         }
36237         
36238         
36239         if (Roo.bootstrap.version == 3) {
36240             this.el.removeClass([this.invalidClass, this.validClass]);
36241             this.el.addClass(this.validClass);
36242         } else {
36243             this.el.removeClass(['is-invalid','is-valid']);
36244             this.el.addClass(['is-valid']);
36245         }
36246         this.fireEvent('valid', this);
36247     },
36248     
36249     markInvalid : function(msg)
36250     {
36251         if(this.allowBlank || this.disabled){
36252             return;
36253         }
36254         
36255         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36256             this.indicatorEl().removeClass('invisible');
36257             this.indicatorEl().addClass('visible');
36258         }
36259         if (Roo.bootstrap.version == 3) {
36260             this.el.removeClass([this.invalidClass, this.validClass]);
36261             this.el.addClass(this.invalidClass);
36262         } else {
36263             this.el.removeClass(['is-invalid','is-valid']);
36264             this.el.addClass(['is-invalid']);
36265         }
36266         
36267         this.fireEvent('invalid', this, msg);
36268         
36269     },
36270     
36271     setValue : function(v, suppressEvent)
36272     {   
36273         if(this.value === v){
36274             return;
36275         }
36276         
36277         this.value = v;
36278         
36279         if(this.rendered){
36280             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36281         }
36282         
36283         Roo.each(this.radioes, function(i){
36284             i.checked = false;
36285             i.el.removeClass('checked');
36286         });
36287         
36288         Roo.each(this.radioes, function(i){
36289             
36290             if(i.value === v || i.value.toString() === v.toString()){
36291                 i.checked = true;
36292                 i.el.addClass('checked');
36293                 
36294                 if(suppressEvent !== true){
36295                     this.fireEvent('check', this, i);
36296                 }
36297                 
36298                 return false;
36299             }
36300             
36301         }, this);
36302         
36303         this.validate();
36304     },
36305     
36306     clearInvalid : function(){
36307         
36308         if(!this.el || this.preventMark){
36309             return;
36310         }
36311         
36312         this.el.removeClass([this.invalidClass]);
36313         
36314         this.fireEvent('valid', this);
36315     }
36316     
36317 });
36318
36319 Roo.apply(Roo.bootstrap.RadioSet, {
36320     
36321     groups: {},
36322     
36323     register : function(set)
36324     {
36325         this.groups[set.name] = set;
36326     },
36327     
36328     get: function(name) 
36329     {
36330         if (typeof(this.groups[name]) == 'undefined') {
36331             return false;
36332         }
36333         
36334         return this.groups[name] ;
36335     }
36336     
36337 });
36338 /*
36339  * Based on:
36340  * Ext JS Library 1.1.1
36341  * Copyright(c) 2006-2007, Ext JS, LLC.
36342  *
36343  * Originally Released Under LGPL - original licence link has changed is not relivant.
36344  *
36345  * Fork - LGPL
36346  * <script type="text/javascript">
36347  */
36348
36349
36350 /**
36351  * @class Roo.bootstrap.SplitBar
36352  * @extends Roo.util.Observable
36353  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36354  * <br><br>
36355  * Usage:
36356  * <pre><code>
36357 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36358                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36359 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36360 split.minSize = 100;
36361 split.maxSize = 600;
36362 split.animate = true;
36363 split.on('moved', splitterMoved);
36364 </code></pre>
36365  * @constructor
36366  * Create a new SplitBar
36367  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36368  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36369  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36370  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36371                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36372                         position of the SplitBar).
36373  */
36374 Roo.bootstrap.SplitBar = function(cfg){
36375     
36376     /** @private */
36377     
36378     //{
36379     //  dragElement : elm
36380     //  resizingElement: el,
36381         // optional..
36382     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36383     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36384         // existingProxy ???
36385     //}
36386     
36387     this.el = Roo.get(cfg.dragElement, true);
36388     this.el.dom.unselectable = "on";
36389     /** @private */
36390     this.resizingEl = Roo.get(cfg.resizingElement, true);
36391
36392     /**
36393      * @private
36394      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36395      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36396      * @type Number
36397      */
36398     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36399     
36400     /**
36401      * The minimum size of the resizing element. (Defaults to 0)
36402      * @type Number
36403      */
36404     this.minSize = 0;
36405     
36406     /**
36407      * The maximum size of the resizing element. (Defaults to 2000)
36408      * @type Number
36409      */
36410     this.maxSize = 2000;
36411     
36412     /**
36413      * Whether to animate the transition to the new size
36414      * @type Boolean
36415      */
36416     this.animate = false;
36417     
36418     /**
36419      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36420      * @type Boolean
36421      */
36422     this.useShim = false;
36423     
36424     /** @private */
36425     this.shim = null;
36426     
36427     if(!cfg.existingProxy){
36428         /** @private */
36429         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36430     }else{
36431         this.proxy = Roo.get(cfg.existingProxy).dom;
36432     }
36433     /** @private */
36434     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36435     
36436     /** @private */
36437     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36438     
36439     /** @private */
36440     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36441     
36442     /** @private */
36443     this.dragSpecs = {};
36444     
36445     /**
36446      * @private The adapter to use to positon and resize elements
36447      */
36448     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36449     this.adapter.init(this);
36450     
36451     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36452         /** @private */
36453         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36454         this.el.addClass("roo-splitbar-h");
36455     }else{
36456         /** @private */
36457         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36458         this.el.addClass("roo-splitbar-v");
36459     }
36460     
36461     this.addEvents({
36462         /**
36463          * @event resize
36464          * Fires when the splitter is moved (alias for {@link #event-moved})
36465          * @param {Roo.bootstrap.SplitBar} this
36466          * @param {Number} newSize the new width or height
36467          */
36468         "resize" : true,
36469         /**
36470          * @event moved
36471          * Fires when the splitter is moved
36472          * @param {Roo.bootstrap.SplitBar} this
36473          * @param {Number} newSize the new width or height
36474          */
36475         "moved" : true,
36476         /**
36477          * @event beforeresize
36478          * Fires before the splitter is dragged
36479          * @param {Roo.bootstrap.SplitBar} this
36480          */
36481         "beforeresize" : true,
36482
36483         "beforeapply" : true
36484     });
36485
36486     Roo.util.Observable.call(this);
36487 };
36488
36489 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36490     onStartProxyDrag : function(x, y){
36491         this.fireEvent("beforeresize", this);
36492         if(!this.overlay){
36493             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36494             o.unselectable();
36495             o.enableDisplayMode("block");
36496             // all splitbars share the same overlay
36497             Roo.bootstrap.SplitBar.prototype.overlay = o;
36498         }
36499         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36500         this.overlay.show();
36501         Roo.get(this.proxy).setDisplayed("block");
36502         var size = this.adapter.getElementSize(this);
36503         this.activeMinSize = this.getMinimumSize();;
36504         this.activeMaxSize = this.getMaximumSize();;
36505         var c1 = size - this.activeMinSize;
36506         var c2 = Math.max(this.activeMaxSize - size, 0);
36507         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36508             this.dd.resetConstraints();
36509             this.dd.setXConstraint(
36510                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36511                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36512             );
36513             this.dd.setYConstraint(0, 0);
36514         }else{
36515             this.dd.resetConstraints();
36516             this.dd.setXConstraint(0, 0);
36517             this.dd.setYConstraint(
36518                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36519                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36520             );
36521          }
36522         this.dragSpecs.startSize = size;
36523         this.dragSpecs.startPoint = [x, y];
36524         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36525     },
36526     
36527     /** 
36528      * @private Called after the drag operation by the DDProxy
36529      */
36530     onEndProxyDrag : function(e){
36531         Roo.get(this.proxy).setDisplayed(false);
36532         var endPoint = Roo.lib.Event.getXY(e);
36533         if(this.overlay){
36534             this.overlay.hide();
36535         }
36536         var newSize;
36537         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36538             newSize = this.dragSpecs.startSize + 
36539                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36540                     endPoint[0] - this.dragSpecs.startPoint[0] :
36541                     this.dragSpecs.startPoint[0] - endPoint[0]
36542                 );
36543         }else{
36544             newSize = this.dragSpecs.startSize + 
36545                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36546                     endPoint[1] - this.dragSpecs.startPoint[1] :
36547                     this.dragSpecs.startPoint[1] - endPoint[1]
36548                 );
36549         }
36550         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36551         if(newSize != this.dragSpecs.startSize){
36552             if(this.fireEvent('beforeapply', this, newSize) !== false){
36553                 this.adapter.setElementSize(this, newSize);
36554                 this.fireEvent("moved", this, newSize);
36555                 this.fireEvent("resize", this, newSize);
36556             }
36557         }
36558     },
36559     
36560     /**
36561      * Get the adapter this SplitBar uses
36562      * @return The adapter object
36563      */
36564     getAdapter : function(){
36565         return this.adapter;
36566     },
36567     
36568     /**
36569      * Set the adapter this SplitBar uses
36570      * @param {Object} adapter A SplitBar adapter object
36571      */
36572     setAdapter : function(adapter){
36573         this.adapter = adapter;
36574         this.adapter.init(this);
36575     },
36576     
36577     /**
36578      * Gets the minimum size for the resizing element
36579      * @return {Number} The minimum size
36580      */
36581     getMinimumSize : function(){
36582         return this.minSize;
36583     },
36584     
36585     /**
36586      * Sets the minimum size for the resizing element
36587      * @param {Number} minSize The minimum size
36588      */
36589     setMinimumSize : function(minSize){
36590         this.minSize = minSize;
36591     },
36592     
36593     /**
36594      * Gets the maximum size for the resizing element
36595      * @return {Number} The maximum size
36596      */
36597     getMaximumSize : function(){
36598         return this.maxSize;
36599     },
36600     
36601     /**
36602      * Sets the maximum size for the resizing element
36603      * @param {Number} maxSize The maximum size
36604      */
36605     setMaximumSize : function(maxSize){
36606         this.maxSize = maxSize;
36607     },
36608     
36609     /**
36610      * Sets the initialize size for the resizing element
36611      * @param {Number} size The initial size
36612      */
36613     setCurrentSize : function(size){
36614         var oldAnimate = this.animate;
36615         this.animate = false;
36616         this.adapter.setElementSize(this, size);
36617         this.animate = oldAnimate;
36618     },
36619     
36620     /**
36621      * Destroy this splitbar. 
36622      * @param {Boolean} removeEl True to remove the element
36623      */
36624     destroy : function(removeEl){
36625         if(this.shim){
36626             this.shim.remove();
36627         }
36628         this.dd.unreg();
36629         this.proxy.parentNode.removeChild(this.proxy);
36630         if(removeEl){
36631             this.el.remove();
36632         }
36633     }
36634 });
36635
36636 /**
36637  * @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.
36638  */
36639 Roo.bootstrap.SplitBar.createProxy = function(dir){
36640     var proxy = new Roo.Element(document.createElement("div"));
36641     proxy.unselectable();
36642     var cls = 'roo-splitbar-proxy';
36643     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36644     document.body.appendChild(proxy.dom);
36645     return proxy.dom;
36646 };
36647
36648 /** 
36649  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36650  * Default Adapter. It assumes the splitter and resizing element are not positioned
36651  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36652  */
36653 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36654 };
36655
36656 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36657     // do nothing for now
36658     init : function(s){
36659     
36660     },
36661     /**
36662      * Called before drag operations to get the current size of the resizing element. 
36663      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36664      */
36665      getElementSize : function(s){
36666         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36667             return s.resizingEl.getWidth();
36668         }else{
36669             return s.resizingEl.getHeight();
36670         }
36671     },
36672     
36673     /**
36674      * Called after drag operations to set the size of the resizing element.
36675      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36676      * @param {Number} newSize The new size to set
36677      * @param {Function} onComplete A function to be invoked when resizing is complete
36678      */
36679     setElementSize : function(s, newSize, onComplete){
36680         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36681             if(!s.animate){
36682                 s.resizingEl.setWidth(newSize);
36683                 if(onComplete){
36684                     onComplete(s, newSize);
36685                 }
36686             }else{
36687                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36688             }
36689         }else{
36690             
36691             if(!s.animate){
36692                 s.resizingEl.setHeight(newSize);
36693                 if(onComplete){
36694                     onComplete(s, newSize);
36695                 }
36696             }else{
36697                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36698             }
36699         }
36700     }
36701 };
36702
36703 /** 
36704  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36705  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36706  * Adapter that  moves the splitter element to align with the resized sizing element. 
36707  * Used with an absolute positioned SplitBar.
36708  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36709  * document.body, make sure you assign an id to the body element.
36710  */
36711 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36712     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36713     this.container = Roo.get(container);
36714 };
36715
36716 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36717     init : function(s){
36718         this.basic.init(s);
36719     },
36720     
36721     getElementSize : function(s){
36722         return this.basic.getElementSize(s);
36723     },
36724     
36725     setElementSize : function(s, newSize, onComplete){
36726         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36727     },
36728     
36729     moveSplitter : function(s){
36730         var yes = Roo.bootstrap.SplitBar;
36731         switch(s.placement){
36732             case yes.LEFT:
36733                 s.el.setX(s.resizingEl.getRight());
36734                 break;
36735             case yes.RIGHT:
36736                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36737                 break;
36738             case yes.TOP:
36739                 s.el.setY(s.resizingEl.getBottom());
36740                 break;
36741             case yes.BOTTOM:
36742                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36743                 break;
36744         }
36745     }
36746 };
36747
36748 /**
36749  * Orientation constant - Create a vertical SplitBar
36750  * @static
36751  * @type Number
36752  */
36753 Roo.bootstrap.SplitBar.VERTICAL = 1;
36754
36755 /**
36756  * Orientation constant - Create a horizontal SplitBar
36757  * @static
36758  * @type Number
36759  */
36760 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36761
36762 /**
36763  * Placement constant - The resizing element is to the left of the splitter element
36764  * @static
36765  * @type Number
36766  */
36767 Roo.bootstrap.SplitBar.LEFT = 1;
36768
36769 /**
36770  * Placement constant - The resizing element is to the right of the splitter element
36771  * @static
36772  * @type Number
36773  */
36774 Roo.bootstrap.SplitBar.RIGHT = 2;
36775
36776 /**
36777  * Placement constant - The resizing element is positioned above the splitter element
36778  * @static
36779  * @type Number
36780  */
36781 Roo.bootstrap.SplitBar.TOP = 3;
36782
36783 /**
36784  * Placement constant - The resizing element is positioned under splitter element
36785  * @static
36786  * @type Number
36787  */
36788 Roo.bootstrap.SplitBar.BOTTOM = 4;
36789 Roo.namespace("Roo.bootstrap.layout");/*
36790  * Based on:
36791  * Ext JS Library 1.1.1
36792  * Copyright(c) 2006-2007, Ext JS, LLC.
36793  *
36794  * Originally Released Under LGPL - original licence link has changed is not relivant.
36795  *
36796  * Fork - LGPL
36797  * <script type="text/javascript">
36798  */
36799
36800 /**
36801  * @class Roo.bootstrap.layout.Manager
36802  * @extends Roo.bootstrap.Component
36803  * Base class for layout managers.
36804  */
36805 Roo.bootstrap.layout.Manager = function(config)
36806 {
36807     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36808
36809
36810
36811
36812
36813     /** false to disable window resize monitoring @type Boolean */
36814     this.monitorWindowResize = true;
36815     this.regions = {};
36816     this.addEvents({
36817         /**
36818          * @event layout
36819          * Fires when a layout is performed.
36820          * @param {Roo.LayoutManager} this
36821          */
36822         "layout" : true,
36823         /**
36824          * @event regionresized
36825          * Fires when the user resizes a region.
36826          * @param {Roo.LayoutRegion} region The resized region
36827          * @param {Number} newSize The new size (width for east/west, height for north/south)
36828          */
36829         "regionresized" : true,
36830         /**
36831          * @event regioncollapsed
36832          * Fires when a region is collapsed.
36833          * @param {Roo.LayoutRegion} region The collapsed region
36834          */
36835         "regioncollapsed" : true,
36836         /**
36837          * @event regionexpanded
36838          * Fires when a region is expanded.
36839          * @param {Roo.LayoutRegion} region The expanded region
36840          */
36841         "regionexpanded" : true
36842     });
36843     this.updating = false;
36844
36845     if (config.el) {
36846         this.el = Roo.get(config.el);
36847         this.initEvents();
36848     }
36849
36850 };
36851
36852 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36853
36854
36855     regions : null,
36856
36857     monitorWindowResize : true,
36858
36859
36860     updating : false,
36861
36862
36863     onRender : function(ct, position)
36864     {
36865         if(!this.el){
36866             this.el = Roo.get(ct);
36867             this.initEvents();
36868         }
36869         //this.fireEvent('render',this);
36870     },
36871
36872
36873     initEvents: function()
36874     {
36875
36876
36877         // ie scrollbar fix
36878         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36879             document.body.scroll = "no";
36880         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36881             this.el.position('relative');
36882         }
36883         this.id = this.el.id;
36884         this.el.addClass("roo-layout-container");
36885         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36886         if(this.el.dom != document.body ) {
36887             this.el.on('resize', this.layout,this);
36888             this.el.on('show', this.layout,this);
36889         }
36890
36891     },
36892
36893     /**
36894      * Returns true if this layout is currently being updated
36895      * @return {Boolean}
36896      */
36897     isUpdating : function(){
36898         return this.updating;
36899     },
36900
36901     /**
36902      * Suspend the LayoutManager from doing auto-layouts while
36903      * making multiple add or remove calls
36904      */
36905     beginUpdate : function(){
36906         this.updating = true;
36907     },
36908
36909     /**
36910      * Restore auto-layouts and optionally disable the manager from performing a layout
36911      * @param {Boolean} noLayout true to disable a layout update
36912      */
36913     endUpdate : function(noLayout){
36914         this.updating = false;
36915         if(!noLayout){
36916             this.layout();
36917         }
36918     },
36919
36920     layout: function(){
36921         // abstract...
36922     },
36923
36924     onRegionResized : function(region, newSize){
36925         this.fireEvent("regionresized", region, newSize);
36926         this.layout();
36927     },
36928
36929     onRegionCollapsed : function(region){
36930         this.fireEvent("regioncollapsed", region);
36931     },
36932
36933     onRegionExpanded : function(region){
36934         this.fireEvent("regionexpanded", region);
36935     },
36936
36937     /**
36938      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36939      * performs box-model adjustments.
36940      * @return {Object} The size as an object {width: (the width), height: (the height)}
36941      */
36942     getViewSize : function()
36943     {
36944         var size;
36945         if(this.el.dom != document.body){
36946             size = this.el.getSize();
36947         }else{
36948             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36949         }
36950         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36951         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36952         return size;
36953     },
36954
36955     /**
36956      * Returns the Element this layout is bound to.
36957      * @return {Roo.Element}
36958      */
36959     getEl : function(){
36960         return this.el;
36961     },
36962
36963     /**
36964      * Returns the specified region.
36965      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36966      * @return {Roo.LayoutRegion}
36967      */
36968     getRegion : function(target){
36969         return this.regions[target.toLowerCase()];
36970     },
36971
36972     onWindowResize : function(){
36973         if(this.monitorWindowResize){
36974             this.layout();
36975         }
36976     }
36977 });
36978 /*
36979  * Based on:
36980  * Ext JS Library 1.1.1
36981  * Copyright(c) 2006-2007, Ext JS, LLC.
36982  *
36983  * Originally Released Under LGPL - original licence link has changed is not relivant.
36984  *
36985  * Fork - LGPL
36986  * <script type="text/javascript">
36987  */
36988 /**
36989  * @class Roo.bootstrap.layout.Border
36990  * @extends Roo.bootstrap.layout.Manager
36991  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36992  * please see: examples/bootstrap/nested.html<br><br>
36993  
36994 <b>The container the layout is rendered into can be either the body element or any other element.
36995 If it is not the body element, the container needs to either be an absolute positioned element,
36996 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36997 the container size if it is not the body element.</b>
36998
36999 * @constructor
37000 * Create a new Border
37001 * @param {Object} config Configuration options
37002  */
37003 Roo.bootstrap.layout.Border = function(config){
37004     config = config || {};
37005     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37006     
37007     
37008     
37009     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37010         if(config[region]){
37011             config[region].region = region;
37012             this.addRegion(config[region]);
37013         }
37014     },this);
37015     
37016 };
37017
37018 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37019
37020 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37021     
37022     parent : false, // this might point to a 'nest' or a ???
37023     
37024     /**
37025      * Creates and adds a new region if it doesn't already exist.
37026      * @param {String} target The target region key (north, south, east, west or center).
37027      * @param {Object} config The regions config object
37028      * @return {BorderLayoutRegion} The new region
37029      */
37030     addRegion : function(config)
37031     {
37032         if(!this.regions[config.region]){
37033             var r = this.factory(config);
37034             this.bindRegion(r);
37035         }
37036         return this.regions[config.region];
37037     },
37038
37039     // private (kinda)
37040     bindRegion : function(r){
37041         this.regions[r.config.region] = r;
37042         
37043         r.on("visibilitychange",    this.layout, this);
37044         r.on("paneladded",          this.layout, this);
37045         r.on("panelremoved",        this.layout, this);
37046         r.on("invalidated",         this.layout, this);
37047         r.on("resized",             this.onRegionResized, this);
37048         r.on("collapsed",           this.onRegionCollapsed, this);
37049         r.on("expanded",            this.onRegionExpanded, this);
37050     },
37051
37052     /**
37053      * Performs a layout update.
37054      */
37055     layout : function()
37056     {
37057         if(this.updating) {
37058             return;
37059         }
37060         
37061         // render all the rebions if they have not been done alreayd?
37062         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37063             if(this.regions[region] && !this.regions[region].bodyEl){
37064                 this.regions[region].onRender(this.el)
37065             }
37066         },this);
37067         
37068         var size = this.getViewSize();
37069         var w = size.width;
37070         var h = size.height;
37071         var centerW = w;
37072         var centerH = h;
37073         var centerY = 0;
37074         var centerX = 0;
37075         //var x = 0, y = 0;
37076
37077         var rs = this.regions;
37078         var north = rs["north"];
37079         var south = rs["south"]; 
37080         var west = rs["west"];
37081         var east = rs["east"];
37082         var center = rs["center"];
37083         //if(this.hideOnLayout){ // not supported anymore
37084             //c.el.setStyle("display", "none");
37085         //}
37086         if(north && north.isVisible()){
37087             var b = north.getBox();
37088             var m = north.getMargins();
37089             b.width = w - (m.left+m.right);
37090             b.x = m.left;
37091             b.y = m.top;
37092             centerY = b.height + b.y + m.bottom;
37093             centerH -= centerY;
37094             north.updateBox(this.safeBox(b));
37095         }
37096         if(south && south.isVisible()){
37097             var b = south.getBox();
37098             var m = south.getMargins();
37099             b.width = w - (m.left+m.right);
37100             b.x = m.left;
37101             var totalHeight = (b.height + m.top + m.bottom);
37102             b.y = h - totalHeight + m.top;
37103             centerH -= totalHeight;
37104             south.updateBox(this.safeBox(b));
37105         }
37106         if(west && west.isVisible()){
37107             var b = west.getBox();
37108             var m = west.getMargins();
37109             b.height = centerH - (m.top+m.bottom);
37110             b.x = m.left;
37111             b.y = centerY + m.top;
37112             var totalWidth = (b.width + m.left + m.right);
37113             centerX += totalWidth;
37114             centerW -= totalWidth;
37115             west.updateBox(this.safeBox(b));
37116         }
37117         if(east && east.isVisible()){
37118             var b = east.getBox();
37119             var m = east.getMargins();
37120             b.height = centerH - (m.top+m.bottom);
37121             var totalWidth = (b.width + m.left + m.right);
37122             b.x = w - totalWidth + m.left;
37123             b.y = centerY + m.top;
37124             centerW -= totalWidth;
37125             east.updateBox(this.safeBox(b));
37126         }
37127         if(center){
37128             var m = center.getMargins();
37129             var centerBox = {
37130                 x: centerX + m.left,
37131                 y: centerY + m.top,
37132                 width: centerW - (m.left+m.right),
37133                 height: centerH - (m.top+m.bottom)
37134             };
37135             //if(this.hideOnLayout){
37136                 //center.el.setStyle("display", "block");
37137             //}
37138             center.updateBox(this.safeBox(centerBox));
37139         }
37140         this.el.repaint();
37141         this.fireEvent("layout", this);
37142     },
37143
37144     // private
37145     safeBox : function(box){
37146         box.width = Math.max(0, box.width);
37147         box.height = Math.max(0, box.height);
37148         return box;
37149     },
37150
37151     /**
37152      * Adds a ContentPanel (or subclass) to this layout.
37153      * @param {String} target The target region key (north, south, east, west or center).
37154      * @param {Roo.ContentPanel} panel The panel to add
37155      * @return {Roo.ContentPanel} The added panel
37156      */
37157     add : function(target, panel){
37158          
37159         target = target.toLowerCase();
37160         return this.regions[target].add(panel);
37161     },
37162
37163     /**
37164      * Remove a ContentPanel (or subclass) to this layout.
37165      * @param {String} target The target region key (north, south, east, west or center).
37166      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37167      * @return {Roo.ContentPanel} The removed panel
37168      */
37169     remove : function(target, panel){
37170         target = target.toLowerCase();
37171         return this.regions[target].remove(panel);
37172     },
37173
37174     /**
37175      * Searches all regions for a panel with the specified id
37176      * @param {String} panelId
37177      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37178      */
37179     findPanel : function(panelId){
37180         var rs = this.regions;
37181         for(var target in rs){
37182             if(typeof rs[target] != "function"){
37183                 var p = rs[target].getPanel(panelId);
37184                 if(p){
37185                     return p;
37186                 }
37187             }
37188         }
37189         return null;
37190     },
37191
37192     /**
37193      * Searches all regions for a panel with the specified id and activates (shows) it.
37194      * @param {String/ContentPanel} panelId The panels id or the panel itself
37195      * @return {Roo.ContentPanel} The shown panel or null
37196      */
37197     showPanel : function(panelId) {
37198       var rs = this.regions;
37199       for(var target in rs){
37200          var r = rs[target];
37201          if(typeof r != "function"){
37202             if(r.hasPanel(panelId)){
37203                return r.showPanel(panelId);
37204             }
37205          }
37206       }
37207       return null;
37208    },
37209
37210    /**
37211      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37212      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37213      */
37214    /*
37215     restoreState : function(provider){
37216         if(!provider){
37217             provider = Roo.state.Manager;
37218         }
37219         var sm = new Roo.LayoutStateManager();
37220         sm.init(this, provider);
37221     },
37222 */
37223  
37224  
37225     /**
37226      * Adds a xtype elements to the layout.
37227      * <pre><code>
37228
37229 layout.addxtype({
37230        xtype : 'ContentPanel',
37231        region: 'west',
37232        items: [ .... ]
37233    }
37234 );
37235
37236 layout.addxtype({
37237         xtype : 'NestedLayoutPanel',
37238         region: 'west',
37239         layout: {
37240            center: { },
37241            west: { }   
37242         },
37243         items : [ ... list of content panels or nested layout panels.. ]
37244    }
37245 );
37246 </code></pre>
37247      * @param {Object} cfg Xtype definition of item to add.
37248      */
37249     addxtype : function(cfg)
37250     {
37251         // basically accepts a pannel...
37252         // can accept a layout region..!?!?
37253         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37254         
37255         
37256         // theory?  children can only be panels??
37257         
37258         //if (!cfg.xtype.match(/Panel$/)) {
37259         //    return false;
37260         //}
37261         var ret = false;
37262         
37263         if (typeof(cfg.region) == 'undefined') {
37264             Roo.log("Failed to add Panel, region was not set");
37265             Roo.log(cfg);
37266             return false;
37267         }
37268         var region = cfg.region;
37269         delete cfg.region;
37270         
37271           
37272         var xitems = [];
37273         if (cfg.items) {
37274             xitems = cfg.items;
37275             delete cfg.items;
37276         }
37277         var nb = false;
37278         
37279         if ( region == 'center') {
37280             Roo.log("Center: " + cfg.title);
37281         }
37282         
37283         
37284         switch(cfg.xtype) 
37285         {
37286             case 'Content':  // ContentPanel (el, cfg)
37287             case 'Scroll':  // ContentPanel (el, cfg)
37288             case 'View': 
37289                 cfg.autoCreate = cfg.autoCreate || true;
37290                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37291                 //} else {
37292                 //    var el = this.el.createChild();
37293                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37294                 //}
37295                 
37296                 this.add(region, ret);
37297                 break;
37298             
37299             /*
37300             case 'TreePanel': // our new panel!
37301                 cfg.el = this.el.createChild();
37302                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37303                 this.add(region, ret);
37304                 break;
37305             */
37306             
37307             case 'Nest': 
37308                 // create a new Layout (which is  a Border Layout...
37309                 
37310                 var clayout = cfg.layout;
37311                 clayout.el  = this.el.createChild();
37312                 clayout.items   = clayout.items  || [];
37313                 
37314                 delete cfg.layout;
37315                 
37316                 // replace this exitems with the clayout ones..
37317                 xitems = clayout.items;
37318                  
37319                 // force background off if it's in center...
37320                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37321                     cfg.background = false;
37322                 }
37323                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37324                 
37325                 
37326                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37327                 //console.log('adding nested layout panel '  + cfg.toSource());
37328                 this.add(region, ret);
37329                 nb = {}; /// find first...
37330                 break;
37331             
37332             case 'Grid':
37333                 
37334                 // needs grid and region
37335                 
37336                 //var el = this.getRegion(region).el.createChild();
37337                 /*
37338                  *var el = this.el.createChild();
37339                 // create the grid first...
37340                 cfg.grid.container = el;
37341                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37342                 */
37343                 
37344                 if (region == 'center' && this.active ) {
37345                     cfg.background = false;
37346                 }
37347                 
37348                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37349                 
37350                 this.add(region, ret);
37351                 /*
37352                 if (cfg.background) {
37353                     // render grid on panel activation (if panel background)
37354                     ret.on('activate', function(gp) {
37355                         if (!gp.grid.rendered) {
37356                     //        gp.grid.render(el);
37357                         }
37358                     });
37359                 } else {
37360                   //  cfg.grid.render(el);
37361                 }
37362                 */
37363                 break;
37364            
37365            
37366             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37367                 // it was the old xcomponent building that caused this before.
37368                 // espeically if border is the top element in the tree.
37369                 ret = this;
37370                 break; 
37371                 
37372                     
37373                 
37374                 
37375                 
37376             default:
37377                 /*
37378                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37379                     
37380                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37381                     this.add(region, ret);
37382                 } else {
37383                 */
37384                     Roo.log(cfg);
37385                     throw "Can not add '" + cfg.xtype + "' to Border";
37386                     return null;
37387              
37388                                 
37389              
37390         }
37391         this.beginUpdate();
37392         // add children..
37393         var region = '';
37394         var abn = {};
37395         Roo.each(xitems, function(i)  {
37396             region = nb && i.region ? i.region : false;
37397             
37398             var add = ret.addxtype(i);
37399            
37400             if (region) {
37401                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37402                 if (!i.background) {
37403                     abn[region] = nb[region] ;
37404                 }
37405             }
37406             
37407         });
37408         this.endUpdate();
37409
37410         // make the last non-background panel active..
37411         //if (nb) { Roo.log(abn); }
37412         if (nb) {
37413             
37414             for(var r in abn) {
37415                 region = this.getRegion(r);
37416                 if (region) {
37417                     // tried using nb[r], but it does not work..
37418                      
37419                     region.showPanel(abn[r]);
37420                    
37421                 }
37422             }
37423         }
37424         return ret;
37425         
37426     },
37427     
37428     
37429 // private
37430     factory : function(cfg)
37431     {
37432         
37433         var validRegions = Roo.bootstrap.layout.Border.regions;
37434
37435         var target = cfg.region;
37436         cfg.mgr = this;
37437         
37438         var r = Roo.bootstrap.layout;
37439         Roo.log(target);
37440         switch(target){
37441             case "north":
37442                 return new r.North(cfg);
37443             case "south":
37444                 return new r.South(cfg);
37445             case "east":
37446                 return new r.East(cfg);
37447             case "west":
37448                 return new r.West(cfg);
37449             case "center":
37450                 return new r.Center(cfg);
37451         }
37452         throw 'Layout region "'+target+'" not supported.';
37453     }
37454     
37455     
37456 });
37457  /*
37458  * Based on:
37459  * Ext JS Library 1.1.1
37460  * Copyright(c) 2006-2007, Ext JS, LLC.
37461  *
37462  * Originally Released Under LGPL - original licence link has changed is not relivant.
37463  *
37464  * Fork - LGPL
37465  * <script type="text/javascript">
37466  */
37467  
37468 /**
37469  * @class Roo.bootstrap.layout.Basic
37470  * @extends Roo.util.Observable
37471  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37472  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37473  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37474  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37475  * @cfg {string}   region  the region that it inhabits..
37476  * @cfg {bool}   skipConfig skip config?
37477  * 
37478
37479  */
37480 Roo.bootstrap.layout.Basic = function(config){
37481     
37482     this.mgr = config.mgr;
37483     
37484     this.position = config.region;
37485     
37486     var skipConfig = config.skipConfig;
37487     
37488     this.events = {
37489         /**
37490          * @scope Roo.BasicLayoutRegion
37491          */
37492         
37493         /**
37494          * @event beforeremove
37495          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37496          * @param {Roo.LayoutRegion} this
37497          * @param {Roo.ContentPanel} panel The panel
37498          * @param {Object} e The cancel event object
37499          */
37500         "beforeremove" : true,
37501         /**
37502          * @event invalidated
37503          * Fires when the layout for this region is changed.
37504          * @param {Roo.LayoutRegion} this
37505          */
37506         "invalidated" : true,
37507         /**
37508          * @event visibilitychange
37509          * Fires when this region is shown or hidden 
37510          * @param {Roo.LayoutRegion} this
37511          * @param {Boolean} visibility true or false
37512          */
37513         "visibilitychange" : true,
37514         /**
37515          * @event paneladded
37516          * Fires when a panel is added. 
37517          * @param {Roo.LayoutRegion} this
37518          * @param {Roo.ContentPanel} panel The panel
37519          */
37520         "paneladded" : true,
37521         /**
37522          * @event panelremoved
37523          * Fires when a panel is removed. 
37524          * @param {Roo.LayoutRegion} this
37525          * @param {Roo.ContentPanel} panel The panel
37526          */
37527         "panelremoved" : true,
37528         /**
37529          * @event beforecollapse
37530          * Fires when this region before collapse.
37531          * @param {Roo.LayoutRegion} this
37532          */
37533         "beforecollapse" : true,
37534         /**
37535          * @event collapsed
37536          * Fires when this region is collapsed.
37537          * @param {Roo.LayoutRegion} this
37538          */
37539         "collapsed" : true,
37540         /**
37541          * @event expanded
37542          * Fires when this region is expanded.
37543          * @param {Roo.LayoutRegion} this
37544          */
37545         "expanded" : true,
37546         /**
37547          * @event slideshow
37548          * Fires when this region is slid into view.
37549          * @param {Roo.LayoutRegion} this
37550          */
37551         "slideshow" : true,
37552         /**
37553          * @event slidehide
37554          * Fires when this region slides out of view. 
37555          * @param {Roo.LayoutRegion} this
37556          */
37557         "slidehide" : true,
37558         /**
37559          * @event panelactivated
37560          * Fires when a panel is activated. 
37561          * @param {Roo.LayoutRegion} this
37562          * @param {Roo.ContentPanel} panel The activated panel
37563          */
37564         "panelactivated" : true,
37565         /**
37566          * @event resized
37567          * Fires when the user resizes this region. 
37568          * @param {Roo.LayoutRegion} this
37569          * @param {Number} newSize The new size (width for east/west, height for north/south)
37570          */
37571         "resized" : true
37572     };
37573     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37574     this.panels = new Roo.util.MixedCollection();
37575     this.panels.getKey = this.getPanelId.createDelegate(this);
37576     this.box = null;
37577     this.activePanel = null;
37578     // ensure listeners are added...
37579     
37580     if (config.listeners || config.events) {
37581         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37582             listeners : config.listeners || {},
37583             events : config.events || {}
37584         });
37585     }
37586     
37587     if(skipConfig !== true){
37588         this.applyConfig(config);
37589     }
37590 };
37591
37592 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37593 {
37594     getPanelId : function(p){
37595         return p.getId();
37596     },
37597     
37598     applyConfig : function(config){
37599         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37600         this.config = config;
37601         
37602     },
37603     
37604     /**
37605      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37606      * the width, for horizontal (north, south) the height.
37607      * @param {Number} newSize The new width or height
37608      */
37609     resizeTo : function(newSize){
37610         var el = this.el ? this.el :
37611                  (this.activePanel ? this.activePanel.getEl() : null);
37612         if(el){
37613             switch(this.position){
37614                 case "east":
37615                 case "west":
37616                     el.setWidth(newSize);
37617                     this.fireEvent("resized", this, newSize);
37618                 break;
37619                 case "north":
37620                 case "south":
37621                     el.setHeight(newSize);
37622                     this.fireEvent("resized", this, newSize);
37623                 break;                
37624             }
37625         }
37626     },
37627     
37628     getBox : function(){
37629         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37630     },
37631     
37632     getMargins : function(){
37633         return this.margins;
37634     },
37635     
37636     updateBox : function(box){
37637         this.box = box;
37638         var el = this.activePanel.getEl();
37639         el.dom.style.left = box.x + "px";
37640         el.dom.style.top = box.y + "px";
37641         this.activePanel.setSize(box.width, box.height);
37642     },
37643     
37644     /**
37645      * Returns the container element for this region.
37646      * @return {Roo.Element}
37647      */
37648     getEl : function(){
37649         return this.activePanel;
37650     },
37651     
37652     /**
37653      * Returns true if this region is currently visible.
37654      * @return {Boolean}
37655      */
37656     isVisible : function(){
37657         return this.activePanel ? true : false;
37658     },
37659     
37660     setActivePanel : function(panel){
37661         panel = this.getPanel(panel);
37662         if(this.activePanel && this.activePanel != panel){
37663             this.activePanel.setActiveState(false);
37664             this.activePanel.getEl().setLeftTop(-10000,-10000);
37665         }
37666         this.activePanel = panel;
37667         panel.setActiveState(true);
37668         if(this.box){
37669             panel.setSize(this.box.width, this.box.height);
37670         }
37671         this.fireEvent("panelactivated", this, panel);
37672         this.fireEvent("invalidated");
37673     },
37674     
37675     /**
37676      * Show the specified panel.
37677      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37678      * @return {Roo.ContentPanel} The shown panel or null
37679      */
37680     showPanel : function(panel){
37681         panel = this.getPanel(panel);
37682         if(panel){
37683             this.setActivePanel(panel);
37684         }
37685         return panel;
37686     },
37687     
37688     /**
37689      * Get the active panel for this region.
37690      * @return {Roo.ContentPanel} The active panel or null
37691      */
37692     getActivePanel : function(){
37693         return this.activePanel;
37694     },
37695     
37696     /**
37697      * Add the passed ContentPanel(s)
37698      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37699      * @return {Roo.ContentPanel} The panel added (if only one was added)
37700      */
37701     add : function(panel){
37702         if(arguments.length > 1){
37703             for(var i = 0, len = arguments.length; i < len; i++) {
37704                 this.add(arguments[i]);
37705             }
37706             return null;
37707         }
37708         if(this.hasPanel(panel)){
37709             this.showPanel(panel);
37710             return panel;
37711         }
37712         var el = panel.getEl();
37713         if(el.dom.parentNode != this.mgr.el.dom){
37714             this.mgr.el.dom.appendChild(el.dom);
37715         }
37716         if(panel.setRegion){
37717             panel.setRegion(this);
37718         }
37719         this.panels.add(panel);
37720         el.setStyle("position", "absolute");
37721         if(!panel.background){
37722             this.setActivePanel(panel);
37723             if(this.config.initialSize && this.panels.getCount()==1){
37724                 this.resizeTo(this.config.initialSize);
37725             }
37726         }
37727         this.fireEvent("paneladded", this, panel);
37728         return panel;
37729     },
37730     
37731     /**
37732      * Returns true if the panel is in this region.
37733      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37734      * @return {Boolean}
37735      */
37736     hasPanel : function(panel){
37737         if(typeof panel == "object"){ // must be panel obj
37738             panel = panel.getId();
37739         }
37740         return this.getPanel(panel) ? true : false;
37741     },
37742     
37743     /**
37744      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37745      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37746      * @param {Boolean} preservePanel Overrides the config preservePanel option
37747      * @return {Roo.ContentPanel} The panel that was removed
37748      */
37749     remove : function(panel, preservePanel){
37750         panel = this.getPanel(panel);
37751         if(!panel){
37752             return null;
37753         }
37754         var e = {};
37755         this.fireEvent("beforeremove", this, panel, e);
37756         if(e.cancel === true){
37757             return null;
37758         }
37759         var panelId = panel.getId();
37760         this.panels.removeKey(panelId);
37761         return panel;
37762     },
37763     
37764     /**
37765      * Returns the panel specified or null if it's not in this region.
37766      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37767      * @return {Roo.ContentPanel}
37768      */
37769     getPanel : function(id){
37770         if(typeof id == "object"){ // must be panel obj
37771             return id;
37772         }
37773         return this.panels.get(id);
37774     },
37775     
37776     /**
37777      * Returns this regions position (north/south/east/west/center).
37778      * @return {String} 
37779      */
37780     getPosition: function(){
37781         return this.position;    
37782     }
37783 });/*
37784  * Based on:
37785  * Ext JS Library 1.1.1
37786  * Copyright(c) 2006-2007, Ext JS, LLC.
37787  *
37788  * Originally Released Under LGPL - original licence link has changed is not relivant.
37789  *
37790  * Fork - LGPL
37791  * <script type="text/javascript">
37792  */
37793  
37794 /**
37795  * @class Roo.bootstrap.layout.Region
37796  * @extends Roo.bootstrap.layout.Basic
37797  * This class represents a region in a layout manager.
37798  
37799  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37800  * @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})
37801  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37802  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37803  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37804  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37805  * @cfg {String}    title           The title for the region (overrides panel titles)
37806  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37807  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37808  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37809  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37810  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37811  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37812  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37813  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37814  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37815  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37816
37817  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37818  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37819  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37820  * @cfg {Number}    width           For East/West panels
37821  * @cfg {Number}    height          For North/South panels
37822  * @cfg {Boolean}   split           To show the splitter
37823  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37824  * 
37825  * @cfg {string}   cls             Extra CSS classes to add to region
37826  * 
37827  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37828  * @cfg {string}   region  the region that it inhabits..
37829  *
37830
37831  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37832  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37833
37834  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37835  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37836  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37837  */
37838 Roo.bootstrap.layout.Region = function(config)
37839 {
37840     this.applyConfig(config);
37841
37842     var mgr = config.mgr;
37843     var pos = config.region;
37844     config.skipConfig = true;
37845     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37846     
37847     if (mgr.el) {
37848         this.onRender(mgr.el);   
37849     }
37850      
37851     this.visible = true;
37852     this.collapsed = false;
37853     this.unrendered_panels = [];
37854 };
37855
37856 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37857
37858     position: '', // set by wrapper (eg. north/south etc..)
37859     unrendered_panels : null,  // unrendered panels.
37860     
37861     tabPosition : false,
37862     
37863     mgr: false, // points to 'Border'
37864     
37865     
37866     createBody : function(){
37867         /** This region's body element 
37868         * @type Roo.Element */
37869         this.bodyEl = this.el.createChild({
37870                 tag: "div",
37871                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37872         });
37873     },
37874
37875     onRender: function(ctr, pos)
37876     {
37877         var dh = Roo.DomHelper;
37878         /** This region's container element 
37879         * @type Roo.Element */
37880         this.el = dh.append(ctr.dom, {
37881                 tag: "div",
37882                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37883             }, true);
37884         /** This region's title element 
37885         * @type Roo.Element */
37886     
37887         this.titleEl = dh.append(this.el.dom,  {
37888                 tag: "div",
37889                 unselectable: "on",
37890                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37891                 children:[
37892                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37893                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37894                 ]
37895             }, true);
37896         
37897         this.titleEl.enableDisplayMode();
37898         /** This region's title text element 
37899         * @type HTMLElement */
37900         this.titleTextEl = this.titleEl.dom.firstChild;
37901         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37902         /*
37903         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37904         this.closeBtn.enableDisplayMode();
37905         this.closeBtn.on("click", this.closeClicked, this);
37906         this.closeBtn.hide();
37907     */
37908         this.createBody(this.config);
37909         if(this.config.hideWhenEmpty){
37910             this.hide();
37911             this.on("paneladded", this.validateVisibility, this);
37912             this.on("panelremoved", this.validateVisibility, this);
37913         }
37914         if(this.autoScroll){
37915             this.bodyEl.setStyle("overflow", "auto");
37916         }else{
37917             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37918         }
37919         //if(c.titlebar !== false){
37920             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37921                 this.titleEl.hide();
37922             }else{
37923                 this.titleEl.show();
37924                 if(this.config.title){
37925                     this.titleTextEl.innerHTML = this.config.title;
37926                 }
37927             }
37928         //}
37929         if(this.config.collapsed){
37930             this.collapse(true);
37931         }
37932         if(this.config.hidden){
37933             this.hide();
37934         }
37935         
37936         if (this.unrendered_panels && this.unrendered_panels.length) {
37937             for (var i =0;i< this.unrendered_panels.length; i++) {
37938                 this.add(this.unrendered_panels[i]);
37939             }
37940             this.unrendered_panels = null;
37941             
37942         }
37943         
37944     },
37945     
37946     applyConfig : function(c)
37947     {
37948         /*
37949          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37950             var dh = Roo.DomHelper;
37951             if(c.titlebar !== false){
37952                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37953                 this.collapseBtn.on("click", this.collapse, this);
37954                 this.collapseBtn.enableDisplayMode();
37955                 /*
37956                 if(c.showPin === true || this.showPin){
37957                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37958                     this.stickBtn.enableDisplayMode();
37959                     this.stickBtn.on("click", this.expand, this);
37960                     this.stickBtn.hide();
37961                 }
37962                 
37963             }
37964             */
37965             /** This region's collapsed element
37966             * @type Roo.Element */
37967             /*
37968              *
37969             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37970                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37971             ]}, true);
37972             
37973             if(c.floatable !== false){
37974                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37975                this.collapsedEl.on("click", this.collapseClick, this);
37976             }
37977
37978             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37979                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37980                    id: "message", unselectable: "on", style:{"float":"left"}});
37981                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37982              }
37983             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37984             this.expandBtn.on("click", this.expand, this);
37985             
37986         }
37987         
37988         if(this.collapseBtn){
37989             this.collapseBtn.setVisible(c.collapsible == true);
37990         }
37991         
37992         this.cmargins = c.cmargins || this.cmargins ||
37993                          (this.position == "west" || this.position == "east" ?
37994                              {top: 0, left: 2, right:2, bottom: 0} :
37995                              {top: 2, left: 0, right:0, bottom: 2});
37996         */
37997         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37998         
37999         
38000         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38001         
38002         this.autoScroll = c.autoScroll || false;
38003         
38004         
38005        
38006         
38007         this.duration = c.duration || .30;
38008         this.slideDuration = c.slideDuration || .45;
38009         this.config = c;
38010        
38011     },
38012     /**
38013      * Returns true if this region is currently visible.
38014      * @return {Boolean}
38015      */
38016     isVisible : function(){
38017         return this.visible;
38018     },
38019
38020     /**
38021      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38022      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38023      */
38024     //setCollapsedTitle : function(title){
38025     //    title = title || "&#160;";
38026      //   if(this.collapsedTitleTextEl){
38027       //      this.collapsedTitleTextEl.innerHTML = title;
38028        // }
38029     //},
38030
38031     getBox : function(){
38032         var b;
38033       //  if(!this.collapsed){
38034             b = this.el.getBox(false, true);
38035        // }else{
38036           //  b = this.collapsedEl.getBox(false, true);
38037         //}
38038         return b;
38039     },
38040
38041     getMargins : function(){
38042         return this.margins;
38043         //return this.collapsed ? this.cmargins : this.margins;
38044     },
38045 /*
38046     highlight : function(){
38047         this.el.addClass("x-layout-panel-dragover");
38048     },
38049
38050     unhighlight : function(){
38051         this.el.removeClass("x-layout-panel-dragover");
38052     },
38053 */
38054     updateBox : function(box)
38055     {
38056         if (!this.bodyEl) {
38057             return; // not rendered yet..
38058         }
38059         
38060         this.box = box;
38061         if(!this.collapsed){
38062             this.el.dom.style.left = box.x + "px";
38063             this.el.dom.style.top = box.y + "px";
38064             this.updateBody(box.width, box.height);
38065         }else{
38066             this.collapsedEl.dom.style.left = box.x + "px";
38067             this.collapsedEl.dom.style.top = box.y + "px";
38068             this.collapsedEl.setSize(box.width, box.height);
38069         }
38070         if(this.tabs){
38071             this.tabs.autoSizeTabs();
38072         }
38073     },
38074
38075     updateBody : function(w, h)
38076     {
38077         if(w !== null){
38078             this.el.setWidth(w);
38079             w -= this.el.getBorderWidth("rl");
38080             if(this.config.adjustments){
38081                 w += this.config.adjustments[0];
38082             }
38083         }
38084         if(h !== null && h > 0){
38085             this.el.setHeight(h);
38086             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38087             h -= this.el.getBorderWidth("tb");
38088             if(this.config.adjustments){
38089                 h += this.config.adjustments[1];
38090             }
38091             this.bodyEl.setHeight(h);
38092             if(this.tabs){
38093                 h = this.tabs.syncHeight(h);
38094             }
38095         }
38096         if(this.panelSize){
38097             w = w !== null ? w : this.panelSize.width;
38098             h = h !== null ? h : this.panelSize.height;
38099         }
38100         if(this.activePanel){
38101             var el = this.activePanel.getEl();
38102             w = w !== null ? w : el.getWidth();
38103             h = h !== null ? h : el.getHeight();
38104             this.panelSize = {width: w, height: h};
38105             this.activePanel.setSize(w, h);
38106         }
38107         if(Roo.isIE && this.tabs){
38108             this.tabs.el.repaint();
38109         }
38110     },
38111
38112     /**
38113      * Returns the container element for this region.
38114      * @return {Roo.Element}
38115      */
38116     getEl : function(){
38117         return this.el;
38118     },
38119
38120     /**
38121      * Hides this region.
38122      */
38123     hide : function(){
38124         //if(!this.collapsed){
38125             this.el.dom.style.left = "-2000px";
38126             this.el.hide();
38127         //}else{
38128          //   this.collapsedEl.dom.style.left = "-2000px";
38129          //   this.collapsedEl.hide();
38130        // }
38131         this.visible = false;
38132         this.fireEvent("visibilitychange", this, false);
38133     },
38134
38135     /**
38136      * Shows this region if it was previously hidden.
38137      */
38138     show : function(){
38139         //if(!this.collapsed){
38140             this.el.show();
38141         //}else{
38142         //    this.collapsedEl.show();
38143        // }
38144         this.visible = true;
38145         this.fireEvent("visibilitychange", this, true);
38146     },
38147 /*
38148     closeClicked : function(){
38149         if(this.activePanel){
38150             this.remove(this.activePanel);
38151         }
38152     },
38153
38154     collapseClick : function(e){
38155         if(this.isSlid){
38156            e.stopPropagation();
38157            this.slideIn();
38158         }else{
38159            e.stopPropagation();
38160            this.slideOut();
38161         }
38162     },
38163 */
38164     /**
38165      * Collapses this region.
38166      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38167      */
38168     /*
38169     collapse : function(skipAnim, skipCheck = false){
38170         if(this.collapsed) {
38171             return;
38172         }
38173         
38174         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38175             
38176             this.collapsed = true;
38177             if(this.split){
38178                 this.split.el.hide();
38179             }
38180             if(this.config.animate && skipAnim !== true){
38181                 this.fireEvent("invalidated", this);
38182                 this.animateCollapse();
38183             }else{
38184                 this.el.setLocation(-20000,-20000);
38185                 this.el.hide();
38186                 this.collapsedEl.show();
38187                 this.fireEvent("collapsed", this);
38188                 this.fireEvent("invalidated", this);
38189             }
38190         }
38191         
38192     },
38193 */
38194     animateCollapse : function(){
38195         // overridden
38196     },
38197
38198     /**
38199      * Expands this region if it was previously collapsed.
38200      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38201      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38202      */
38203     /*
38204     expand : function(e, skipAnim){
38205         if(e) {
38206             e.stopPropagation();
38207         }
38208         if(!this.collapsed || this.el.hasActiveFx()) {
38209             return;
38210         }
38211         if(this.isSlid){
38212             this.afterSlideIn();
38213             skipAnim = true;
38214         }
38215         this.collapsed = false;
38216         if(this.config.animate && skipAnim !== true){
38217             this.animateExpand();
38218         }else{
38219             this.el.show();
38220             if(this.split){
38221                 this.split.el.show();
38222             }
38223             this.collapsedEl.setLocation(-2000,-2000);
38224             this.collapsedEl.hide();
38225             this.fireEvent("invalidated", this);
38226             this.fireEvent("expanded", this);
38227         }
38228     },
38229 */
38230     animateExpand : function(){
38231         // overridden
38232     },
38233
38234     initTabs : function()
38235     {
38236         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38237         
38238         var ts = new Roo.bootstrap.panel.Tabs({
38239             el: this.bodyEl.dom,
38240             region : this,
38241             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38242             disableTooltips: this.config.disableTabTips,
38243             toolbar : this.config.toolbar
38244         });
38245         
38246         if(this.config.hideTabs){
38247             ts.stripWrap.setDisplayed(false);
38248         }
38249         this.tabs = ts;
38250         ts.resizeTabs = this.config.resizeTabs === true;
38251         ts.minTabWidth = this.config.minTabWidth || 40;
38252         ts.maxTabWidth = this.config.maxTabWidth || 250;
38253         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38254         ts.monitorResize = false;
38255         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38256         ts.bodyEl.addClass('roo-layout-tabs-body');
38257         this.panels.each(this.initPanelAsTab, this);
38258     },
38259
38260     initPanelAsTab : function(panel){
38261         var ti = this.tabs.addTab(
38262             panel.getEl().id,
38263             panel.getTitle(),
38264             null,
38265             this.config.closeOnTab && panel.isClosable(),
38266             panel.tpl
38267         );
38268         if(panel.tabTip !== undefined){
38269             ti.setTooltip(panel.tabTip);
38270         }
38271         ti.on("activate", function(){
38272               this.setActivePanel(panel);
38273         }, this);
38274         
38275         if(this.config.closeOnTab){
38276             ti.on("beforeclose", function(t, e){
38277                 e.cancel = true;
38278                 this.remove(panel);
38279             }, this);
38280         }
38281         
38282         panel.tabItem = ti;
38283         
38284         return ti;
38285     },
38286
38287     updatePanelTitle : function(panel, title)
38288     {
38289         if(this.activePanel == panel){
38290             this.updateTitle(title);
38291         }
38292         if(this.tabs){
38293             var ti = this.tabs.getTab(panel.getEl().id);
38294             ti.setText(title);
38295             if(panel.tabTip !== undefined){
38296                 ti.setTooltip(panel.tabTip);
38297             }
38298         }
38299     },
38300
38301     updateTitle : function(title){
38302         if(this.titleTextEl && !this.config.title){
38303             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38304         }
38305     },
38306
38307     setActivePanel : function(panel)
38308     {
38309         panel = this.getPanel(panel);
38310         if(this.activePanel && this.activePanel != panel){
38311             if(this.activePanel.setActiveState(false) === false){
38312                 return;
38313             }
38314         }
38315         this.activePanel = panel;
38316         panel.setActiveState(true);
38317         if(this.panelSize){
38318             panel.setSize(this.panelSize.width, this.panelSize.height);
38319         }
38320         if(this.closeBtn){
38321             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38322         }
38323         this.updateTitle(panel.getTitle());
38324         if(this.tabs){
38325             this.fireEvent("invalidated", this);
38326         }
38327         this.fireEvent("panelactivated", this, panel);
38328     },
38329
38330     /**
38331      * Shows the specified panel.
38332      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38333      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38334      */
38335     showPanel : function(panel)
38336     {
38337         panel = this.getPanel(panel);
38338         if(panel){
38339             if(this.tabs){
38340                 var tab = this.tabs.getTab(panel.getEl().id);
38341                 if(tab.isHidden()){
38342                     this.tabs.unhideTab(tab.id);
38343                 }
38344                 tab.activate();
38345             }else{
38346                 this.setActivePanel(panel);
38347             }
38348         }
38349         return panel;
38350     },
38351
38352     /**
38353      * Get the active panel for this region.
38354      * @return {Roo.ContentPanel} The active panel or null
38355      */
38356     getActivePanel : function(){
38357         return this.activePanel;
38358     },
38359
38360     validateVisibility : function(){
38361         if(this.panels.getCount() < 1){
38362             this.updateTitle("&#160;");
38363             this.closeBtn.hide();
38364             this.hide();
38365         }else{
38366             if(!this.isVisible()){
38367                 this.show();
38368             }
38369         }
38370     },
38371
38372     /**
38373      * Adds the passed ContentPanel(s) to this region.
38374      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38375      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38376      */
38377     add : function(panel)
38378     {
38379         if(arguments.length > 1){
38380             for(var i = 0, len = arguments.length; i < len; i++) {
38381                 this.add(arguments[i]);
38382             }
38383             return null;
38384         }
38385         
38386         // if we have not been rendered yet, then we can not really do much of this..
38387         if (!this.bodyEl) {
38388             this.unrendered_panels.push(panel);
38389             return panel;
38390         }
38391         
38392         
38393         
38394         
38395         if(this.hasPanel(panel)){
38396             this.showPanel(panel);
38397             return panel;
38398         }
38399         panel.setRegion(this);
38400         this.panels.add(panel);
38401        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38402             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38403             // and hide them... ???
38404             this.bodyEl.dom.appendChild(panel.getEl().dom);
38405             if(panel.background !== true){
38406                 this.setActivePanel(panel);
38407             }
38408             this.fireEvent("paneladded", this, panel);
38409             return panel;
38410         }
38411         */
38412         if(!this.tabs){
38413             this.initTabs();
38414         }else{
38415             this.initPanelAsTab(panel);
38416         }
38417         
38418         
38419         if(panel.background !== true){
38420             this.tabs.activate(panel.getEl().id);
38421         }
38422         this.fireEvent("paneladded", this, panel);
38423         return panel;
38424     },
38425
38426     /**
38427      * Hides the tab for the specified panel.
38428      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38429      */
38430     hidePanel : function(panel){
38431         if(this.tabs && (panel = this.getPanel(panel))){
38432             this.tabs.hideTab(panel.getEl().id);
38433         }
38434     },
38435
38436     /**
38437      * Unhides the tab for a previously hidden panel.
38438      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38439      */
38440     unhidePanel : function(panel){
38441         if(this.tabs && (panel = this.getPanel(panel))){
38442             this.tabs.unhideTab(panel.getEl().id);
38443         }
38444     },
38445
38446     clearPanels : function(){
38447         while(this.panels.getCount() > 0){
38448              this.remove(this.panels.first());
38449         }
38450     },
38451
38452     /**
38453      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38454      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38455      * @param {Boolean} preservePanel Overrides the config preservePanel option
38456      * @return {Roo.ContentPanel} The panel that was removed
38457      */
38458     remove : function(panel, preservePanel)
38459     {
38460         panel = this.getPanel(panel);
38461         if(!panel){
38462             return null;
38463         }
38464         var e = {};
38465         this.fireEvent("beforeremove", this, panel, e);
38466         if(e.cancel === true){
38467             return null;
38468         }
38469         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38470         var panelId = panel.getId();
38471         this.panels.removeKey(panelId);
38472         if(preservePanel){
38473             document.body.appendChild(panel.getEl().dom);
38474         }
38475         if(this.tabs){
38476             this.tabs.removeTab(panel.getEl().id);
38477         }else if (!preservePanel){
38478             this.bodyEl.dom.removeChild(panel.getEl().dom);
38479         }
38480         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38481             var p = this.panels.first();
38482             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38483             tempEl.appendChild(p.getEl().dom);
38484             this.bodyEl.update("");
38485             this.bodyEl.dom.appendChild(p.getEl().dom);
38486             tempEl = null;
38487             this.updateTitle(p.getTitle());
38488             this.tabs = null;
38489             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38490             this.setActivePanel(p);
38491         }
38492         panel.setRegion(null);
38493         if(this.activePanel == panel){
38494             this.activePanel = null;
38495         }
38496         if(this.config.autoDestroy !== false && preservePanel !== true){
38497             try{panel.destroy();}catch(e){}
38498         }
38499         this.fireEvent("panelremoved", this, panel);
38500         return panel;
38501     },
38502
38503     /**
38504      * Returns the TabPanel component used by this region
38505      * @return {Roo.TabPanel}
38506      */
38507     getTabs : function(){
38508         return this.tabs;
38509     },
38510
38511     createTool : function(parentEl, className){
38512         var btn = Roo.DomHelper.append(parentEl, {
38513             tag: "div",
38514             cls: "x-layout-tools-button",
38515             children: [ {
38516                 tag: "div",
38517                 cls: "roo-layout-tools-button-inner " + className,
38518                 html: "&#160;"
38519             }]
38520         }, true);
38521         btn.addClassOnOver("roo-layout-tools-button-over");
38522         return btn;
38523     }
38524 });/*
38525  * Based on:
38526  * Ext JS Library 1.1.1
38527  * Copyright(c) 2006-2007, Ext JS, LLC.
38528  *
38529  * Originally Released Under LGPL - original licence link has changed is not relivant.
38530  *
38531  * Fork - LGPL
38532  * <script type="text/javascript">
38533  */
38534  
38535
38536
38537 /**
38538  * @class Roo.SplitLayoutRegion
38539  * @extends Roo.LayoutRegion
38540  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38541  */
38542 Roo.bootstrap.layout.Split = function(config){
38543     this.cursor = config.cursor;
38544     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38545 };
38546
38547 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38548 {
38549     splitTip : "Drag to resize.",
38550     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38551     useSplitTips : false,
38552
38553     applyConfig : function(config){
38554         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38555     },
38556     
38557     onRender : function(ctr,pos) {
38558         
38559         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38560         if(!this.config.split){
38561             return;
38562         }
38563         if(!this.split){
38564             
38565             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38566                             tag: "div",
38567                             id: this.el.id + "-split",
38568                             cls: "roo-layout-split roo-layout-split-"+this.position,
38569                             html: "&#160;"
38570             });
38571             /** The SplitBar for this region 
38572             * @type Roo.SplitBar */
38573             // does not exist yet...
38574             Roo.log([this.position, this.orientation]);
38575             
38576             this.split = new Roo.bootstrap.SplitBar({
38577                 dragElement : splitEl,
38578                 resizingElement: this.el,
38579                 orientation : this.orientation
38580             });
38581             
38582             this.split.on("moved", this.onSplitMove, this);
38583             this.split.useShim = this.config.useShim === true;
38584             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38585             if(this.useSplitTips){
38586                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38587             }
38588             //if(config.collapsible){
38589             //    this.split.el.on("dblclick", this.collapse,  this);
38590             //}
38591         }
38592         if(typeof this.config.minSize != "undefined"){
38593             this.split.minSize = this.config.minSize;
38594         }
38595         if(typeof this.config.maxSize != "undefined"){
38596             this.split.maxSize = this.config.maxSize;
38597         }
38598         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38599             this.hideSplitter();
38600         }
38601         
38602     },
38603
38604     getHMaxSize : function(){
38605          var cmax = this.config.maxSize || 10000;
38606          var center = this.mgr.getRegion("center");
38607          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38608     },
38609
38610     getVMaxSize : function(){
38611          var cmax = this.config.maxSize || 10000;
38612          var center = this.mgr.getRegion("center");
38613          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38614     },
38615
38616     onSplitMove : function(split, newSize){
38617         this.fireEvent("resized", this, newSize);
38618     },
38619     
38620     /** 
38621      * Returns the {@link Roo.SplitBar} for this region.
38622      * @return {Roo.SplitBar}
38623      */
38624     getSplitBar : function(){
38625         return this.split;
38626     },
38627     
38628     hide : function(){
38629         this.hideSplitter();
38630         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38631     },
38632
38633     hideSplitter : function(){
38634         if(this.split){
38635             this.split.el.setLocation(-2000,-2000);
38636             this.split.el.hide();
38637         }
38638     },
38639
38640     show : function(){
38641         if(this.split){
38642             this.split.el.show();
38643         }
38644         Roo.bootstrap.layout.Split.superclass.show.call(this);
38645     },
38646     
38647     beforeSlide: function(){
38648         if(Roo.isGecko){// firefox overflow auto bug workaround
38649             this.bodyEl.clip();
38650             if(this.tabs) {
38651                 this.tabs.bodyEl.clip();
38652             }
38653             if(this.activePanel){
38654                 this.activePanel.getEl().clip();
38655                 
38656                 if(this.activePanel.beforeSlide){
38657                     this.activePanel.beforeSlide();
38658                 }
38659             }
38660         }
38661     },
38662     
38663     afterSlide : function(){
38664         if(Roo.isGecko){// firefox overflow auto bug workaround
38665             this.bodyEl.unclip();
38666             if(this.tabs) {
38667                 this.tabs.bodyEl.unclip();
38668             }
38669             if(this.activePanel){
38670                 this.activePanel.getEl().unclip();
38671                 if(this.activePanel.afterSlide){
38672                     this.activePanel.afterSlide();
38673                 }
38674             }
38675         }
38676     },
38677
38678     initAutoHide : function(){
38679         if(this.autoHide !== false){
38680             if(!this.autoHideHd){
38681                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38682                 this.autoHideHd = {
38683                     "mouseout": function(e){
38684                         if(!e.within(this.el, true)){
38685                             st.delay(500);
38686                         }
38687                     },
38688                     "mouseover" : function(e){
38689                         st.cancel();
38690                     },
38691                     scope : this
38692                 };
38693             }
38694             this.el.on(this.autoHideHd);
38695         }
38696     },
38697
38698     clearAutoHide : function(){
38699         if(this.autoHide !== false){
38700             this.el.un("mouseout", this.autoHideHd.mouseout);
38701             this.el.un("mouseover", this.autoHideHd.mouseover);
38702         }
38703     },
38704
38705     clearMonitor : function(){
38706         Roo.get(document).un("click", this.slideInIf, this);
38707     },
38708
38709     // these names are backwards but not changed for compat
38710     slideOut : function(){
38711         if(this.isSlid || this.el.hasActiveFx()){
38712             return;
38713         }
38714         this.isSlid = true;
38715         if(this.collapseBtn){
38716             this.collapseBtn.hide();
38717         }
38718         this.closeBtnState = this.closeBtn.getStyle('display');
38719         this.closeBtn.hide();
38720         if(this.stickBtn){
38721             this.stickBtn.show();
38722         }
38723         this.el.show();
38724         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38725         this.beforeSlide();
38726         this.el.setStyle("z-index", 10001);
38727         this.el.slideIn(this.getSlideAnchor(), {
38728             callback: function(){
38729                 this.afterSlide();
38730                 this.initAutoHide();
38731                 Roo.get(document).on("click", this.slideInIf, this);
38732                 this.fireEvent("slideshow", this);
38733             },
38734             scope: this,
38735             block: true
38736         });
38737     },
38738
38739     afterSlideIn : function(){
38740         this.clearAutoHide();
38741         this.isSlid = false;
38742         this.clearMonitor();
38743         this.el.setStyle("z-index", "");
38744         if(this.collapseBtn){
38745             this.collapseBtn.show();
38746         }
38747         this.closeBtn.setStyle('display', this.closeBtnState);
38748         if(this.stickBtn){
38749             this.stickBtn.hide();
38750         }
38751         this.fireEvent("slidehide", this);
38752     },
38753
38754     slideIn : function(cb){
38755         if(!this.isSlid || this.el.hasActiveFx()){
38756             Roo.callback(cb);
38757             return;
38758         }
38759         this.isSlid = false;
38760         this.beforeSlide();
38761         this.el.slideOut(this.getSlideAnchor(), {
38762             callback: function(){
38763                 this.el.setLeftTop(-10000, -10000);
38764                 this.afterSlide();
38765                 this.afterSlideIn();
38766                 Roo.callback(cb);
38767             },
38768             scope: this,
38769             block: true
38770         });
38771     },
38772     
38773     slideInIf : function(e){
38774         if(!e.within(this.el)){
38775             this.slideIn();
38776         }
38777     },
38778
38779     animateCollapse : function(){
38780         this.beforeSlide();
38781         this.el.setStyle("z-index", 20000);
38782         var anchor = this.getSlideAnchor();
38783         this.el.slideOut(anchor, {
38784             callback : function(){
38785                 this.el.setStyle("z-index", "");
38786                 this.collapsedEl.slideIn(anchor, {duration:.3});
38787                 this.afterSlide();
38788                 this.el.setLocation(-10000,-10000);
38789                 this.el.hide();
38790                 this.fireEvent("collapsed", this);
38791             },
38792             scope: this,
38793             block: true
38794         });
38795     },
38796
38797     animateExpand : function(){
38798         this.beforeSlide();
38799         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38800         this.el.setStyle("z-index", 20000);
38801         this.collapsedEl.hide({
38802             duration:.1
38803         });
38804         this.el.slideIn(this.getSlideAnchor(), {
38805             callback : function(){
38806                 this.el.setStyle("z-index", "");
38807                 this.afterSlide();
38808                 if(this.split){
38809                     this.split.el.show();
38810                 }
38811                 this.fireEvent("invalidated", this);
38812                 this.fireEvent("expanded", this);
38813             },
38814             scope: this,
38815             block: true
38816         });
38817     },
38818
38819     anchors : {
38820         "west" : "left",
38821         "east" : "right",
38822         "north" : "top",
38823         "south" : "bottom"
38824     },
38825
38826     sanchors : {
38827         "west" : "l",
38828         "east" : "r",
38829         "north" : "t",
38830         "south" : "b"
38831     },
38832
38833     canchors : {
38834         "west" : "tl-tr",
38835         "east" : "tr-tl",
38836         "north" : "tl-bl",
38837         "south" : "bl-tl"
38838     },
38839
38840     getAnchor : function(){
38841         return this.anchors[this.position];
38842     },
38843
38844     getCollapseAnchor : function(){
38845         return this.canchors[this.position];
38846     },
38847
38848     getSlideAnchor : function(){
38849         return this.sanchors[this.position];
38850     },
38851
38852     getAlignAdj : function(){
38853         var cm = this.cmargins;
38854         switch(this.position){
38855             case "west":
38856                 return [0, 0];
38857             break;
38858             case "east":
38859                 return [0, 0];
38860             break;
38861             case "north":
38862                 return [0, 0];
38863             break;
38864             case "south":
38865                 return [0, 0];
38866             break;
38867         }
38868     },
38869
38870     getExpandAdj : function(){
38871         var c = this.collapsedEl, cm = this.cmargins;
38872         switch(this.position){
38873             case "west":
38874                 return [-(cm.right+c.getWidth()+cm.left), 0];
38875             break;
38876             case "east":
38877                 return [cm.right+c.getWidth()+cm.left, 0];
38878             break;
38879             case "north":
38880                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38881             break;
38882             case "south":
38883                 return [0, cm.top+cm.bottom+c.getHeight()];
38884             break;
38885         }
38886     }
38887 });/*
38888  * Based on:
38889  * Ext JS Library 1.1.1
38890  * Copyright(c) 2006-2007, Ext JS, LLC.
38891  *
38892  * Originally Released Under LGPL - original licence link has changed is not relivant.
38893  *
38894  * Fork - LGPL
38895  * <script type="text/javascript">
38896  */
38897 /*
38898  * These classes are private internal classes
38899  */
38900 Roo.bootstrap.layout.Center = function(config){
38901     config.region = "center";
38902     Roo.bootstrap.layout.Region.call(this, config);
38903     this.visible = true;
38904     this.minWidth = config.minWidth || 20;
38905     this.minHeight = config.minHeight || 20;
38906 };
38907
38908 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38909     hide : function(){
38910         // center panel can't be hidden
38911     },
38912     
38913     show : function(){
38914         // center panel can't be hidden
38915     },
38916     
38917     getMinWidth: function(){
38918         return this.minWidth;
38919     },
38920     
38921     getMinHeight: function(){
38922         return this.minHeight;
38923     }
38924 });
38925
38926
38927
38928
38929  
38930
38931
38932
38933
38934
38935
38936 Roo.bootstrap.layout.North = function(config)
38937 {
38938     config.region = 'north';
38939     config.cursor = 'n-resize';
38940     
38941     Roo.bootstrap.layout.Split.call(this, config);
38942     
38943     
38944     if(this.split){
38945         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38946         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38947         this.split.el.addClass("roo-layout-split-v");
38948     }
38949     var size = config.initialSize || config.height;
38950     if(typeof size != "undefined"){
38951         this.el.setHeight(size);
38952     }
38953 };
38954 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38955 {
38956     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38957     
38958     
38959     
38960     getBox : function(){
38961         if(this.collapsed){
38962             return this.collapsedEl.getBox();
38963         }
38964         var box = this.el.getBox();
38965         if(this.split){
38966             box.height += this.split.el.getHeight();
38967         }
38968         return box;
38969     },
38970     
38971     updateBox : function(box){
38972         if(this.split && !this.collapsed){
38973             box.height -= this.split.el.getHeight();
38974             this.split.el.setLeft(box.x);
38975             this.split.el.setTop(box.y+box.height);
38976             this.split.el.setWidth(box.width);
38977         }
38978         if(this.collapsed){
38979             this.updateBody(box.width, null);
38980         }
38981         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38982     }
38983 });
38984
38985
38986
38987
38988
38989 Roo.bootstrap.layout.South = function(config){
38990     config.region = 'south';
38991     config.cursor = 's-resize';
38992     Roo.bootstrap.layout.Split.call(this, config);
38993     if(this.split){
38994         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38995         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38996         this.split.el.addClass("roo-layout-split-v");
38997     }
38998     var size = config.initialSize || config.height;
38999     if(typeof size != "undefined"){
39000         this.el.setHeight(size);
39001     }
39002 };
39003
39004 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39005     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39006     getBox : function(){
39007         if(this.collapsed){
39008             return this.collapsedEl.getBox();
39009         }
39010         var box = this.el.getBox();
39011         if(this.split){
39012             var sh = this.split.el.getHeight();
39013             box.height += sh;
39014             box.y -= sh;
39015         }
39016         return box;
39017     },
39018     
39019     updateBox : function(box){
39020         if(this.split && !this.collapsed){
39021             var sh = this.split.el.getHeight();
39022             box.height -= sh;
39023             box.y += sh;
39024             this.split.el.setLeft(box.x);
39025             this.split.el.setTop(box.y-sh);
39026             this.split.el.setWidth(box.width);
39027         }
39028         if(this.collapsed){
39029             this.updateBody(box.width, null);
39030         }
39031         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39032     }
39033 });
39034
39035 Roo.bootstrap.layout.East = function(config){
39036     config.region = "east";
39037     config.cursor = "e-resize";
39038     Roo.bootstrap.layout.Split.call(this, config);
39039     if(this.split){
39040         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39041         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39042         this.split.el.addClass("roo-layout-split-h");
39043     }
39044     var size = config.initialSize || config.width;
39045     if(typeof size != "undefined"){
39046         this.el.setWidth(size);
39047     }
39048 };
39049 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39050     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39051     getBox : function(){
39052         if(this.collapsed){
39053             return this.collapsedEl.getBox();
39054         }
39055         var box = this.el.getBox();
39056         if(this.split){
39057             var sw = this.split.el.getWidth();
39058             box.width += sw;
39059             box.x -= sw;
39060         }
39061         return box;
39062     },
39063
39064     updateBox : function(box){
39065         if(this.split && !this.collapsed){
39066             var sw = this.split.el.getWidth();
39067             box.width -= sw;
39068             this.split.el.setLeft(box.x);
39069             this.split.el.setTop(box.y);
39070             this.split.el.setHeight(box.height);
39071             box.x += sw;
39072         }
39073         if(this.collapsed){
39074             this.updateBody(null, box.height);
39075         }
39076         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39077     }
39078 });
39079
39080 Roo.bootstrap.layout.West = function(config){
39081     config.region = "west";
39082     config.cursor = "w-resize";
39083     
39084     Roo.bootstrap.layout.Split.call(this, config);
39085     if(this.split){
39086         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39087         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39088         this.split.el.addClass("roo-layout-split-h");
39089     }
39090     
39091 };
39092 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39093     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39094     
39095     onRender: function(ctr, pos)
39096     {
39097         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39098         var size = this.config.initialSize || this.config.width;
39099         if(typeof size != "undefined"){
39100             this.el.setWidth(size);
39101         }
39102     },
39103     
39104     getBox : function(){
39105         if(this.collapsed){
39106             return this.collapsedEl.getBox();
39107         }
39108         var box = this.el.getBox();
39109         if(this.split){
39110             box.width += this.split.el.getWidth();
39111         }
39112         return box;
39113     },
39114     
39115     updateBox : function(box){
39116         if(this.split && !this.collapsed){
39117             var sw = this.split.el.getWidth();
39118             box.width -= sw;
39119             this.split.el.setLeft(box.x+box.width);
39120             this.split.el.setTop(box.y);
39121             this.split.el.setHeight(box.height);
39122         }
39123         if(this.collapsed){
39124             this.updateBody(null, box.height);
39125         }
39126         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39127     }
39128 });Roo.namespace("Roo.bootstrap.panel");/*
39129  * Based on:
39130  * Ext JS Library 1.1.1
39131  * Copyright(c) 2006-2007, Ext JS, LLC.
39132  *
39133  * Originally Released Under LGPL - original licence link has changed is not relivant.
39134  *
39135  * Fork - LGPL
39136  * <script type="text/javascript">
39137  */
39138 /**
39139  * @class Roo.ContentPanel
39140  * @extends Roo.util.Observable
39141  * A basic ContentPanel element.
39142  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39143  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39144  * @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
39145  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39146  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39147  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39148  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39149  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39150  * @cfg {String} title          The title for this panel
39151  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39152  * @cfg {String} url            Calls {@link #setUrl} with this value
39153  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39154  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39155  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39156  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39157  * @cfg {Boolean} badges render the badges
39158  * @cfg {String} cls  extra classes to use  
39159  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39160
39161  * @constructor
39162  * Create a new ContentPanel.
39163  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39164  * @param {String/Object} config A string to set only the title or a config object
39165  * @param {String} content (optional) Set the HTML content for this panel
39166  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39167  */
39168 Roo.bootstrap.panel.Content = function( config){
39169     
39170     this.tpl = config.tpl || false;
39171     
39172     var el = config.el;
39173     var content = config.content;
39174
39175     if(config.autoCreate){ // xtype is available if this is called from factory
39176         el = Roo.id();
39177     }
39178     this.el = Roo.get(el);
39179     if(!this.el && config && config.autoCreate){
39180         if(typeof config.autoCreate == "object"){
39181             if(!config.autoCreate.id){
39182                 config.autoCreate.id = config.id||el;
39183             }
39184             this.el = Roo.DomHelper.append(document.body,
39185                         config.autoCreate, true);
39186         }else{
39187             var elcfg =  {
39188                 tag: "div",
39189                 cls: (config.cls || '') +
39190                     (config.background ? ' bg-' + config.background : '') +
39191                     " roo-layout-inactive-content",
39192                 id: config.id||el
39193             };
39194             if (config.html) {
39195                 elcfg.html = config.html;
39196                 
39197             }
39198                         
39199             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39200         }
39201     } 
39202     this.closable = false;
39203     this.loaded = false;
39204     this.active = false;
39205    
39206       
39207     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39208         
39209         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39210         
39211         this.wrapEl = this.el; //this.el.wrap();
39212         var ti = [];
39213         if (config.toolbar.items) {
39214             ti = config.toolbar.items ;
39215             delete config.toolbar.items ;
39216         }
39217         
39218         var nitems = [];
39219         this.toolbar.render(this.wrapEl, 'before');
39220         for(var i =0;i < ti.length;i++) {
39221           //  Roo.log(['add child', items[i]]);
39222             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39223         }
39224         this.toolbar.items = nitems;
39225         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39226         delete config.toolbar;
39227         
39228     }
39229     /*
39230     // xtype created footer. - not sure if will work as we normally have to render first..
39231     if (this.footer && !this.footer.el && this.footer.xtype) {
39232         if (!this.wrapEl) {
39233             this.wrapEl = this.el.wrap();
39234         }
39235     
39236         this.footer.container = this.wrapEl.createChild();
39237          
39238         this.footer = Roo.factory(this.footer, Roo);
39239         
39240     }
39241     */
39242     
39243      if(typeof config == "string"){
39244         this.title = config;
39245     }else{
39246         Roo.apply(this, config);
39247     }
39248     
39249     if(this.resizeEl){
39250         this.resizeEl = Roo.get(this.resizeEl, true);
39251     }else{
39252         this.resizeEl = this.el;
39253     }
39254     // handle view.xtype
39255     
39256  
39257     
39258     
39259     this.addEvents({
39260         /**
39261          * @event activate
39262          * Fires when this panel is activated. 
39263          * @param {Roo.ContentPanel} this
39264          */
39265         "activate" : true,
39266         /**
39267          * @event deactivate
39268          * Fires when this panel is activated. 
39269          * @param {Roo.ContentPanel} this
39270          */
39271         "deactivate" : true,
39272
39273         /**
39274          * @event resize
39275          * Fires when this panel is resized if fitToFrame is true.
39276          * @param {Roo.ContentPanel} this
39277          * @param {Number} width The width after any component adjustments
39278          * @param {Number} height The height after any component adjustments
39279          */
39280         "resize" : true,
39281         
39282          /**
39283          * @event render
39284          * Fires when this tab is created
39285          * @param {Roo.ContentPanel} this
39286          */
39287         "render" : true
39288         
39289         
39290         
39291     });
39292     
39293
39294     
39295     
39296     if(this.autoScroll){
39297         this.resizeEl.setStyle("overflow", "auto");
39298     } else {
39299         // fix randome scrolling
39300         //this.el.on('scroll', function() {
39301         //    Roo.log('fix random scolling');
39302         //    this.scrollTo('top',0); 
39303         //});
39304     }
39305     content = content || this.content;
39306     if(content){
39307         this.setContent(content);
39308     }
39309     if(config && config.url){
39310         this.setUrl(this.url, this.params, this.loadOnce);
39311     }
39312     
39313     
39314     
39315     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39316     
39317     if (this.view && typeof(this.view.xtype) != 'undefined') {
39318         this.view.el = this.el.appendChild(document.createElement("div"));
39319         this.view = Roo.factory(this.view); 
39320         this.view.render  &&  this.view.render(false, '');  
39321     }
39322     
39323     
39324     this.fireEvent('render', this);
39325 };
39326
39327 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39328     
39329     cls : '',
39330     background : '',
39331     
39332     tabTip : '',
39333     
39334     setRegion : function(region){
39335         this.region = region;
39336         this.setActiveClass(region && !this.background);
39337     },
39338     
39339     
39340     setActiveClass: function(state)
39341     {
39342         if(state){
39343            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39344            this.el.setStyle('position','relative');
39345         }else{
39346            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39347            this.el.setStyle('position', 'absolute');
39348         } 
39349     },
39350     
39351     /**
39352      * Returns the toolbar for this Panel if one was configured. 
39353      * @return {Roo.Toolbar} 
39354      */
39355     getToolbar : function(){
39356         return this.toolbar;
39357     },
39358     
39359     setActiveState : function(active)
39360     {
39361         this.active = active;
39362         this.setActiveClass(active);
39363         if(!active){
39364             if(this.fireEvent("deactivate", this) === false){
39365                 return false;
39366             }
39367             return true;
39368         }
39369         this.fireEvent("activate", this);
39370         return true;
39371     },
39372     /**
39373      * Updates this panel's element
39374      * @param {String} content The new content
39375      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39376     */
39377     setContent : function(content, loadScripts){
39378         this.el.update(content, loadScripts);
39379     },
39380
39381     ignoreResize : function(w, h){
39382         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39383             return true;
39384         }else{
39385             this.lastSize = {width: w, height: h};
39386             return false;
39387         }
39388     },
39389     /**
39390      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39391      * @return {Roo.UpdateManager} The UpdateManager
39392      */
39393     getUpdateManager : function(){
39394         return this.el.getUpdateManager();
39395     },
39396      /**
39397      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39398      * @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:
39399 <pre><code>
39400 panel.load({
39401     url: "your-url.php",
39402     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39403     callback: yourFunction,
39404     scope: yourObject, //(optional scope)
39405     discardUrl: false,
39406     nocache: false,
39407     text: "Loading...",
39408     timeout: 30,
39409     scripts: false
39410 });
39411 </code></pre>
39412      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39413      * 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.
39414      * @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}
39415      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39416      * @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.
39417      * @return {Roo.ContentPanel} this
39418      */
39419     load : function(){
39420         var um = this.el.getUpdateManager();
39421         um.update.apply(um, arguments);
39422         return this;
39423     },
39424
39425
39426     /**
39427      * 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.
39428      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39429      * @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)
39430      * @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)
39431      * @return {Roo.UpdateManager} The UpdateManager
39432      */
39433     setUrl : function(url, params, loadOnce){
39434         if(this.refreshDelegate){
39435             this.removeListener("activate", this.refreshDelegate);
39436         }
39437         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39438         this.on("activate", this.refreshDelegate);
39439         return this.el.getUpdateManager();
39440     },
39441     
39442     _handleRefresh : function(url, params, loadOnce){
39443         if(!loadOnce || !this.loaded){
39444             var updater = this.el.getUpdateManager();
39445             updater.update(url, params, this._setLoaded.createDelegate(this));
39446         }
39447     },
39448     
39449     _setLoaded : function(){
39450         this.loaded = true;
39451     }, 
39452     
39453     /**
39454      * Returns this panel's id
39455      * @return {String} 
39456      */
39457     getId : function(){
39458         return this.el.id;
39459     },
39460     
39461     /** 
39462      * Returns this panel's element - used by regiosn to add.
39463      * @return {Roo.Element} 
39464      */
39465     getEl : function(){
39466         return this.wrapEl || this.el;
39467     },
39468     
39469    
39470     
39471     adjustForComponents : function(width, height)
39472     {
39473         //Roo.log('adjustForComponents ');
39474         if(this.resizeEl != this.el){
39475             width -= this.el.getFrameWidth('lr');
39476             height -= this.el.getFrameWidth('tb');
39477         }
39478         if(this.toolbar){
39479             var te = this.toolbar.getEl();
39480             te.setWidth(width);
39481             height -= te.getHeight();
39482         }
39483         if(this.footer){
39484             var te = this.footer.getEl();
39485             te.setWidth(width);
39486             height -= te.getHeight();
39487         }
39488         
39489         
39490         if(this.adjustments){
39491             width += this.adjustments[0];
39492             height += this.adjustments[1];
39493         }
39494         return {"width": width, "height": height};
39495     },
39496     
39497     setSize : function(width, height){
39498         if(this.fitToFrame && !this.ignoreResize(width, height)){
39499             if(this.fitContainer && this.resizeEl != this.el){
39500                 this.el.setSize(width, height);
39501             }
39502             var size = this.adjustForComponents(width, height);
39503             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39504             this.fireEvent('resize', this, size.width, size.height);
39505         }
39506     },
39507     
39508     /**
39509      * Returns this panel's title
39510      * @return {String} 
39511      */
39512     getTitle : function(){
39513         
39514         if (typeof(this.title) != 'object') {
39515             return this.title;
39516         }
39517         
39518         var t = '';
39519         for (var k in this.title) {
39520             if (!this.title.hasOwnProperty(k)) {
39521                 continue;
39522             }
39523             
39524             if (k.indexOf('-') >= 0) {
39525                 var s = k.split('-');
39526                 for (var i = 0; i<s.length; i++) {
39527                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39528                 }
39529             } else {
39530                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39531             }
39532         }
39533         return t;
39534     },
39535     
39536     /**
39537      * Set this panel's title
39538      * @param {String} title
39539      */
39540     setTitle : function(title){
39541         this.title = title;
39542         if(this.region){
39543             this.region.updatePanelTitle(this, title);
39544         }
39545     },
39546     
39547     /**
39548      * Returns true is this panel was configured to be closable
39549      * @return {Boolean} 
39550      */
39551     isClosable : function(){
39552         return this.closable;
39553     },
39554     
39555     beforeSlide : function(){
39556         this.el.clip();
39557         this.resizeEl.clip();
39558     },
39559     
39560     afterSlide : function(){
39561         this.el.unclip();
39562         this.resizeEl.unclip();
39563     },
39564     
39565     /**
39566      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39567      *   Will fail silently if the {@link #setUrl} method has not been called.
39568      *   This does not activate the panel, just updates its content.
39569      */
39570     refresh : function(){
39571         if(this.refreshDelegate){
39572            this.loaded = false;
39573            this.refreshDelegate();
39574         }
39575     },
39576     
39577     /**
39578      * Destroys this panel
39579      */
39580     destroy : function(){
39581         this.el.removeAllListeners();
39582         var tempEl = document.createElement("span");
39583         tempEl.appendChild(this.el.dom);
39584         tempEl.innerHTML = "";
39585         this.el.remove();
39586         this.el = null;
39587     },
39588     
39589     /**
39590      * form - if the content panel contains a form - this is a reference to it.
39591      * @type {Roo.form.Form}
39592      */
39593     form : false,
39594     /**
39595      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39596      *    This contains a reference to it.
39597      * @type {Roo.View}
39598      */
39599     view : false,
39600     
39601       /**
39602      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39603      * <pre><code>
39604
39605 layout.addxtype({
39606        xtype : 'Form',
39607        items: [ .... ]
39608    }
39609 );
39610
39611 </code></pre>
39612      * @param {Object} cfg Xtype definition of item to add.
39613      */
39614     
39615     
39616     getChildContainer: function () {
39617         return this.getEl();
39618     }
39619     
39620     
39621     /*
39622         var  ret = new Roo.factory(cfg);
39623         return ret;
39624         
39625         
39626         // add form..
39627         if (cfg.xtype.match(/^Form$/)) {
39628             
39629             var el;
39630             //if (this.footer) {
39631             //    el = this.footer.container.insertSibling(false, 'before');
39632             //} else {
39633                 el = this.el.createChild();
39634             //}
39635
39636             this.form = new  Roo.form.Form(cfg);
39637             
39638             
39639             if ( this.form.allItems.length) {
39640                 this.form.render(el.dom);
39641             }
39642             return this.form;
39643         }
39644         // should only have one of theses..
39645         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39646             // views.. should not be just added - used named prop 'view''
39647             
39648             cfg.el = this.el.appendChild(document.createElement("div"));
39649             // factory?
39650             
39651             var ret = new Roo.factory(cfg);
39652              
39653              ret.render && ret.render(false, ''); // render blank..
39654             this.view = ret;
39655             return ret;
39656         }
39657         return false;
39658     }
39659     \*/
39660 });
39661  
39662 /**
39663  * @class Roo.bootstrap.panel.Grid
39664  * @extends Roo.bootstrap.panel.Content
39665  * @constructor
39666  * Create a new GridPanel.
39667  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39668  * @param {Object} config A the config object
39669   
39670  */
39671
39672
39673
39674 Roo.bootstrap.panel.Grid = function(config)
39675 {
39676     
39677       
39678     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39679         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39680
39681     config.el = this.wrapper;
39682     //this.el = this.wrapper;
39683     
39684       if (config.container) {
39685         // ctor'ed from a Border/panel.grid
39686         
39687         
39688         this.wrapper.setStyle("overflow", "hidden");
39689         this.wrapper.addClass('roo-grid-container');
39690
39691     }
39692     
39693     
39694     if(config.toolbar){
39695         var tool_el = this.wrapper.createChild();    
39696         this.toolbar = Roo.factory(config.toolbar);
39697         var ti = [];
39698         if (config.toolbar.items) {
39699             ti = config.toolbar.items ;
39700             delete config.toolbar.items ;
39701         }
39702         
39703         var nitems = [];
39704         this.toolbar.render(tool_el);
39705         for(var i =0;i < ti.length;i++) {
39706           //  Roo.log(['add child', items[i]]);
39707             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39708         }
39709         this.toolbar.items = nitems;
39710         
39711         delete config.toolbar;
39712     }
39713     
39714     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39715     config.grid.scrollBody = true;;
39716     config.grid.monitorWindowResize = false; // turn off autosizing
39717     config.grid.autoHeight = false;
39718     config.grid.autoWidth = false;
39719     
39720     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39721     
39722     if (config.background) {
39723         // render grid on panel activation (if panel background)
39724         this.on('activate', function(gp) {
39725             if (!gp.grid.rendered) {
39726                 gp.grid.render(this.wrapper);
39727                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39728             }
39729         });
39730             
39731     } else {
39732         this.grid.render(this.wrapper);
39733         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39734
39735     }
39736     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39737     // ??? needed ??? config.el = this.wrapper;
39738     
39739     
39740     
39741   
39742     // xtype created footer. - not sure if will work as we normally have to render first..
39743     if (this.footer && !this.footer.el && this.footer.xtype) {
39744         
39745         var ctr = this.grid.getView().getFooterPanel(true);
39746         this.footer.dataSource = this.grid.dataSource;
39747         this.footer = Roo.factory(this.footer, Roo);
39748         this.footer.render(ctr);
39749         
39750     }
39751     
39752     
39753     
39754     
39755      
39756 };
39757
39758 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39759     getId : function(){
39760         return this.grid.id;
39761     },
39762     
39763     /**
39764      * Returns the grid for this panel
39765      * @return {Roo.bootstrap.Table} 
39766      */
39767     getGrid : function(){
39768         return this.grid;    
39769     },
39770     
39771     setSize : function(width, height){
39772         if(!this.ignoreResize(width, height)){
39773             var grid = this.grid;
39774             var size = this.adjustForComponents(width, height);
39775             // tfoot is not a footer?
39776           
39777             
39778             var gridel = grid.getGridEl();
39779             gridel.setSize(size.width, size.height);
39780             
39781             var tbd = grid.getGridEl().select('tbody', true).first();
39782             var thd = grid.getGridEl().select('thead',true).first();
39783             var tbf= grid.getGridEl().select('tfoot', true).first();
39784
39785             if (tbf) {
39786                 size.height -= thd.getHeight();
39787             }
39788             if (thd) {
39789                 size.height -= thd.getHeight();
39790             }
39791             
39792             tbd.setSize(size.width, size.height );
39793             // this is for the account management tab -seems to work there.
39794             var thd = grid.getGridEl().select('thead',true).first();
39795             //if (tbd) {
39796             //    tbd.setSize(size.width, size.height - thd.getHeight());
39797             //}
39798              
39799             grid.autoSize();
39800         }
39801     },
39802      
39803     
39804     
39805     beforeSlide : function(){
39806         this.grid.getView().scroller.clip();
39807     },
39808     
39809     afterSlide : function(){
39810         this.grid.getView().scroller.unclip();
39811     },
39812     
39813     destroy : function(){
39814         this.grid.destroy();
39815         delete this.grid;
39816         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39817     }
39818 });
39819
39820 /**
39821  * @class Roo.bootstrap.panel.Nest
39822  * @extends Roo.bootstrap.panel.Content
39823  * @constructor
39824  * Create a new Panel, that can contain a layout.Border.
39825  * 
39826  * 
39827  * @param {Roo.BorderLayout} layout The layout for this panel
39828  * @param {String/Object} config A string to set only the title or a config object
39829  */
39830 Roo.bootstrap.panel.Nest = function(config)
39831 {
39832     // construct with only one argument..
39833     /* FIXME - implement nicer consturctors
39834     if (layout.layout) {
39835         config = layout;
39836         layout = config.layout;
39837         delete config.layout;
39838     }
39839     if (layout.xtype && !layout.getEl) {
39840         // then layout needs constructing..
39841         layout = Roo.factory(layout, Roo);
39842     }
39843     */
39844     
39845     config.el =  config.layout.getEl();
39846     
39847     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39848     
39849     config.layout.monitorWindowResize = false; // turn off autosizing
39850     this.layout = config.layout;
39851     this.layout.getEl().addClass("roo-layout-nested-layout");
39852     this.layout.parent = this;
39853     
39854     
39855     
39856     
39857 };
39858
39859 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39860
39861     setSize : function(width, height){
39862         if(!this.ignoreResize(width, height)){
39863             var size = this.adjustForComponents(width, height);
39864             var el = this.layout.getEl();
39865             if (size.height < 1) {
39866                 el.setWidth(size.width);   
39867             } else {
39868                 el.setSize(size.width, size.height);
39869             }
39870             var touch = el.dom.offsetWidth;
39871             this.layout.layout();
39872             // ie requires a double layout on the first pass
39873             if(Roo.isIE && !this.initialized){
39874                 this.initialized = true;
39875                 this.layout.layout();
39876             }
39877         }
39878     },
39879     
39880     // activate all subpanels if not currently active..
39881     
39882     setActiveState : function(active){
39883         this.active = active;
39884         this.setActiveClass(active);
39885         
39886         if(!active){
39887             this.fireEvent("deactivate", this);
39888             return;
39889         }
39890         
39891         this.fireEvent("activate", this);
39892         // not sure if this should happen before or after..
39893         if (!this.layout) {
39894             return; // should not happen..
39895         }
39896         var reg = false;
39897         for (var r in this.layout.regions) {
39898             reg = this.layout.getRegion(r);
39899             if (reg.getActivePanel()) {
39900                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39901                 reg.setActivePanel(reg.getActivePanel());
39902                 continue;
39903             }
39904             if (!reg.panels.length) {
39905                 continue;
39906             }
39907             reg.showPanel(reg.getPanel(0));
39908         }
39909         
39910         
39911         
39912         
39913     },
39914     
39915     /**
39916      * Returns the nested BorderLayout for this panel
39917      * @return {Roo.BorderLayout} 
39918      */
39919     getLayout : function(){
39920         return this.layout;
39921     },
39922     
39923      /**
39924      * Adds a xtype elements to the layout of the nested panel
39925      * <pre><code>
39926
39927 panel.addxtype({
39928        xtype : 'ContentPanel',
39929        region: 'west',
39930        items: [ .... ]
39931    }
39932 );
39933
39934 panel.addxtype({
39935         xtype : 'NestedLayoutPanel',
39936         region: 'west',
39937         layout: {
39938            center: { },
39939            west: { }   
39940         },
39941         items : [ ... list of content panels or nested layout panels.. ]
39942    }
39943 );
39944 </code></pre>
39945      * @param {Object} cfg Xtype definition of item to add.
39946      */
39947     addxtype : function(cfg) {
39948         return this.layout.addxtype(cfg);
39949     
39950     }
39951 });/*
39952  * Based on:
39953  * Ext JS Library 1.1.1
39954  * Copyright(c) 2006-2007, Ext JS, LLC.
39955  *
39956  * Originally Released Under LGPL - original licence link has changed is not relivant.
39957  *
39958  * Fork - LGPL
39959  * <script type="text/javascript">
39960  */
39961 /**
39962  * @class Roo.TabPanel
39963  * @extends Roo.util.Observable
39964  * A lightweight tab container.
39965  * <br><br>
39966  * Usage:
39967  * <pre><code>
39968 // basic tabs 1, built from existing content
39969 var tabs = new Roo.TabPanel("tabs1");
39970 tabs.addTab("script", "View Script");
39971 tabs.addTab("markup", "View Markup");
39972 tabs.activate("script");
39973
39974 // more advanced tabs, built from javascript
39975 var jtabs = new Roo.TabPanel("jtabs");
39976 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39977
39978 // set up the UpdateManager
39979 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39980 var updater = tab2.getUpdateManager();
39981 updater.setDefaultUrl("ajax1.htm");
39982 tab2.on('activate', updater.refresh, updater, true);
39983
39984 // Use setUrl for Ajax loading
39985 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39986 tab3.setUrl("ajax2.htm", null, true);
39987
39988 // Disabled tab
39989 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39990 tab4.disable();
39991
39992 jtabs.activate("jtabs-1");
39993  * </code></pre>
39994  * @constructor
39995  * Create a new TabPanel.
39996  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39997  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39998  */
39999 Roo.bootstrap.panel.Tabs = function(config){
40000     /**
40001     * The container element for this TabPanel.
40002     * @type Roo.Element
40003     */
40004     this.el = Roo.get(config.el);
40005     delete config.el;
40006     if(config){
40007         if(typeof config == "boolean"){
40008             this.tabPosition = config ? "bottom" : "top";
40009         }else{
40010             Roo.apply(this, config);
40011         }
40012     }
40013     
40014     if(this.tabPosition == "bottom"){
40015         // if tabs are at the bottom = create the body first.
40016         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40017         this.el.addClass("roo-tabs-bottom");
40018     }
40019     // next create the tabs holders
40020     
40021     if (this.tabPosition == "west"){
40022         
40023         var reg = this.region; // fake it..
40024         while (reg) {
40025             if (!reg.mgr.parent) {
40026                 break;
40027             }
40028             reg = reg.mgr.parent.region;
40029         }
40030         Roo.log("got nest?");
40031         Roo.log(reg);
40032         if (reg.mgr.getRegion('west')) {
40033             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40034             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40035             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40036             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40037             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40038         
40039             
40040         }
40041         
40042         
40043     } else {
40044      
40045         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40046         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40047         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40048         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40049     }
40050     
40051     
40052     if(Roo.isIE){
40053         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40054     }
40055     
40056     // finally - if tabs are at the top, then create the body last..
40057     if(this.tabPosition != "bottom"){
40058         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40059          * @type Roo.Element
40060          */
40061         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40062         this.el.addClass("roo-tabs-top");
40063     }
40064     this.items = [];
40065
40066     this.bodyEl.setStyle("position", "relative");
40067
40068     this.active = null;
40069     this.activateDelegate = this.activate.createDelegate(this);
40070
40071     this.addEvents({
40072         /**
40073          * @event tabchange
40074          * Fires when the active tab changes
40075          * @param {Roo.TabPanel} this
40076          * @param {Roo.TabPanelItem} activePanel The new active tab
40077          */
40078         "tabchange": true,
40079         /**
40080          * @event beforetabchange
40081          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40082          * @param {Roo.TabPanel} this
40083          * @param {Object} e Set cancel to true on this object to cancel the tab change
40084          * @param {Roo.TabPanelItem} tab The tab being changed to
40085          */
40086         "beforetabchange" : true
40087     });
40088
40089     Roo.EventManager.onWindowResize(this.onResize, this);
40090     this.cpad = this.el.getPadding("lr");
40091     this.hiddenCount = 0;
40092
40093
40094     // toolbar on the tabbar support...
40095     if (this.toolbar) {
40096         alert("no toolbar support yet");
40097         this.toolbar  = false;
40098         /*
40099         var tcfg = this.toolbar;
40100         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40101         this.toolbar = new Roo.Toolbar(tcfg);
40102         if (Roo.isSafari) {
40103             var tbl = tcfg.container.child('table', true);
40104             tbl.setAttribute('width', '100%');
40105         }
40106         */
40107         
40108     }
40109    
40110
40111
40112     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40113 };
40114
40115 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40116     /*
40117      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40118      */
40119     tabPosition : "top",
40120     /*
40121      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40122      */
40123     currentTabWidth : 0,
40124     /*
40125      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40126      */
40127     minTabWidth : 40,
40128     /*
40129      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40130      */
40131     maxTabWidth : 250,
40132     /*
40133      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40134      */
40135     preferredTabWidth : 175,
40136     /*
40137      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40138      */
40139     resizeTabs : false,
40140     /*
40141      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40142      */
40143     monitorResize : true,
40144     /*
40145      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40146      */
40147     toolbar : false,  // set by caller..
40148     
40149     region : false, /// set by caller
40150     
40151     disableTooltips : true, // not used yet...
40152
40153     /**
40154      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40155      * @param {String} id The id of the div to use <b>or create</b>
40156      * @param {String} text The text for the tab
40157      * @param {String} content (optional) Content to put in the TabPanelItem body
40158      * @param {Boolean} closable (optional) True to create a close icon on the tab
40159      * @return {Roo.TabPanelItem} The created TabPanelItem
40160      */
40161     addTab : function(id, text, content, closable, tpl)
40162     {
40163         var item = new Roo.bootstrap.panel.TabItem({
40164             panel: this,
40165             id : id,
40166             text : text,
40167             closable : closable,
40168             tpl : tpl
40169         });
40170         this.addTabItem(item);
40171         if(content){
40172             item.setContent(content);
40173         }
40174         return item;
40175     },
40176
40177     /**
40178      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40179      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40180      * @return {Roo.TabPanelItem}
40181      */
40182     getTab : function(id){
40183         return this.items[id];
40184     },
40185
40186     /**
40187      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40188      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40189      */
40190     hideTab : function(id){
40191         var t = this.items[id];
40192         if(!t.isHidden()){
40193            t.setHidden(true);
40194            this.hiddenCount++;
40195            this.autoSizeTabs();
40196         }
40197     },
40198
40199     /**
40200      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40201      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40202      */
40203     unhideTab : function(id){
40204         var t = this.items[id];
40205         if(t.isHidden()){
40206            t.setHidden(false);
40207            this.hiddenCount--;
40208            this.autoSizeTabs();
40209         }
40210     },
40211
40212     /**
40213      * Adds an existing {@link Roo.TabPanelItem}.
40214      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40215      */
40216     addTabItem : function(item)
40217     {
40218         this.items[item.id] = item;
40219         this.items.push(item);
40220         this.autoSizeTabs();
40221       //  if(this.resizeTabs){
40222     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40223   //         this.autoSizeTabs();
40224 //        }else{
40225 //            item.autoSize();
40226        // }
40227     },
40228
40229     /**
40230      * Removes a {@link Roo.TabPanelItem}.
40231      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40232      */
40233     removeTab : function(id){
40234         var items = this.items;
40235         var tab = items[id];
40236         if(!tab) { return; }
40237         var index = items.indexOf(tab);
40238         if(this.active == tab && items.length > 1){
40239             var newTab = this.getNextAvailable(index);
40240             if(newTab) {
40241                 newTab.activate();
40242             }
40243         }
40244         this.stripEl.dom.removeChild(tab.pnode.dom);
40245         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40246             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40247         }
40248         items.splice(index, 1);
40249         delete this.items[tab.id];
40250         tab.fireEvent("close", tab);
40251         tab.purgeListeners();
40252         this.autoSizeTabs();
40253     },
40254
40255     getNextAvailable : function(start){
40256         var items = this.items;
40257         var index = start;
40258         // look for a next tab that will slide over to
40259         // replace the one being removed
40260         while(index < items.length){
40261             var item = items[++index];
40262             if(item && !item.isHidden()){
40263                 return item;
40264             }
40265         }
40266         // if one isn't found select the previous tab (on the left)
40267         index = start;
40268         while(index >= 0){
40269             var item = items[--index];
40270             if(item && !item.isHidden()){
40271                 return item;
40272             }
40273         }
40274         return null;
40275     },
40276
40277     /**
40278      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40279      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40280      */
40281     disableTab : function(id){
40282         var tab = this.items[id];
40283         if(tab && this.active != tab){
40284             tab.disable();
40285         }
40286     },
40287
40288     /**
40289      * Enables a {@link Roo.TabPanelItem} that is disabled.
40290      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40291      */
40292     enableTab : function(id){
40293         var tab = this.items[id];
40294         tab.enable();
40295     },
40296
40297     /**
40298      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40299      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40300      * @return {Roo.TabPanelItem} The TabPanelItem.
40301      */
40302     activate : function(id)
40303     {
40304         //Roo.log('activite:'  + id);
40305         
40306         var tab = this.items[id];
40307         if(!tab){
40308             return null;
40309         }
40310         if(tab == this.active || tab.disabled){
40311             return tab;
40312         }
40313         var e = {};
40314         this.fireEvent("beforetabchange", this, e, tab);
40315         if(e.cancel !== true && !tab.disabled){
40316             if(this.active){
40317                 this.active.hide();
40318             }
40319             this.active = this.items[id];
40320             this.active.show();
40321             this.fireEvent("tabchange", this, this.active);
40322         }
40323         return tab;
40324     },
40325
40326     /**
40327      * Gets the active {@link Roo.TabPanelItem}.
40328      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40329      */
40330     getActiveTab : function(){
40331         return this.active;
40332     },
40333
40334     /**
40335      * Updates the tab body element to fit the height of the container element
40336      * for overflow scrolling
40337      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40338      */
40339     syncHeight : function(targetHeight){
40340         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40341         var bm = this.bodyEl.getMargins();
40342         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40343         this.bodyEl.setHeight(newHeight);
40344         return newHeight;
40345     },
40346
40347     onResize : function(){
40348         if(this.monitorResize){
40349             this.autoSizeTabs();
40350         }
40351     },
40352
40353     /**
40354      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40355      */
40356     beginUpdate : function(){
40357         this.updating = true;
40358     },
40359
40360     /**
40361      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40362      */
40363     endUpdate : function(){
40364         this.updating = false;
40365         this.autoSizeTabs();
40366     },
40367
40368     /**
40369      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40370      */
40371     autoSizeTabs : function()
40372     {
40373         var count = this.items.length;
40374         var vcount = count - this.hiddenCount;
40375         
40376         if (vcount < 2) {
40377             this.stripEl.hide();
40378         } else {
40379             this.stripEl.show();
40380         }
40381         
40382         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40383             return;
40384         }
40385         
40386         
40387         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40388         var availWidth = Math.floor(w / vcount);
40389         var b = this.stripBody;
40390         if(b.getWidth() > w){
40391             var tabs = this.items;
40392             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40393             if(availWidth < this.minTabWidth){
40394                 /*if(!this.sleft){    // incomplete scrolling code
40395                     this.createScrollButtons();
40396                 }
40397                 this.showScroll();
40398                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40399             }
40400         }else{
40401             if(this.currentTabWidth < this.preferredTabWidth){
40402                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40403             }
40404         }
40405     },
40406
40407     /**
40408      * Returns the number of tabs in this TabPanel.
40409      * @return {Number}
40410      */
40411      getCount : function(){
40412          return this.items.length;
40413      },
40414
40415     /**
40416      * Resizes all the tabs to the passed width
40417      * @param {Number} The new width
40418      */
40419     setTabWidth : function(width){
40420         this.currentTabWidth = width;
40421         for(var i = 0, len = this.items.length; i < len; i++) {
40422                 if(!this.items[i].isHidden()) {
40423                 this.items[i].setWidth(width);
40424             }
40425         }
40426     },
40427
40428     /**
40429      * Destroys this TabPanel
40430      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40431      */
40432     destroy : function(removeEl){
40433         Roo.EventManager.removeResizeListener(this.onResize, this);
40434         for(var i = 0, len = this.items.length; i < len; i++){
40435             this.items[i].purgeListeners();
40436         }
40437         if(removeEl === true){
40438             this.el.update("");
40439             this.el.remove();
40440         }
40441     },
40442     
40443     createStrip : function(container)
40444     {
40445         var strip = document.createElement("nav");
40446         strip.className = Roo.bootstrap.version == 4 ?
40447             "navbar-light bg-light" : 
40448             "navbar navbar-default"; //"x-tabs-wrap";
40449         container.appendChild(strip);
40450         return strip;
40451     },
40452     
40453     createStripList : function(strip)
40454     {
40455         // div wrapper for retard IE
40456         // returns the "tr" element.
40457         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40458         //'<div class="x-tabs-strip-wrap">'+
40459           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40460           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40461         return strip.firstChild; //.firstChild.firstChild.firstChild;
40462     },
40463     createBody : function(container)
40464     {
40465         var body = document.createElement("div");
40466         Roo.id(body, "tab-body");
40467         //Roo.fly(body).addClass("x-tabs-body");
40468         Roo.fly(body).addClass("tab-content");
40469         container.appendChild(body);
40470         return body;
40471     },
40472     createItemBody :function(bodyEl, id){
40473         var body = Roo.getDom(id);
40474         if(!body){
40475             body = document.createElement("div");
40476             body.id = id;
40477         }
40478         //Roo.fly(body).addClass("x-tabs-item-body");
40479         Roo.fly(body).addClass("tab-pane");
40480          bodyEl.insertBefore(body, bodyEl.firstChild);
40481         return body;
40482     },
40483     /** @private */
40484     createStripElements :  function(stripEl, text, closable, tpl)
40485     {
40486         var td = document.createElement("li"); // was td..
40487         td.className = 'nav-item';
40488         
40489         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40490         
40491         
40492         stripEl.appendChild(td);
40493         /*if(closable){
40494             td.className = "x-tabs-closable";
40495             if(!this.closeTpl){
40496                 this.closeTpl = new Roo.Template(
40497                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40498                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40499                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40500                 );
40501             }
40502             var el = this.closeTpl.overwrite(td, {"text": text});
40503             var close = el.getElementsByTagName("div")[0];
40504             var inner = el.getElementsByTagName("em")[0];
40505             return {"el": el, "close": close, "inner": inner};
40506         } else {
40507         */
40508         // not sure what this is..
40509 //            if(!this.tabTpl){
40510                 //this.tabTpl = new Roo.Template(
40511                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40512                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40513                 //);
40514 //                this.tabTpl = new Roo.Template(
40515 //                   '<a href="#">' +
40516 //                   '<span unselectable="on"' +
40517 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40518 //                            ' >{text}</span></a>'
40519 //                );
40520 //                
40521 //            }
40522
40523
40524             var template = tpl || this.tabTpl || false;
40525             
40526             if(!template){
40527                 template =  new Roo.Template(
40528                         Roo.bootstrap.version == 4 ? 
40529                             (
40530                                 '<a class="nav-link" href="#" unselectable="on"' +
40531                                      (this.disableTooltips ? '' : ' title="{text}"') +
40532                                      ' >{text}</a>'
40533                             ) : (
40534                                 '<a class="nav-link" href="#">' +
40535                                 '<span unselectable="on"' +
40536                                          (this.disableTooltips ? '' : ' title="{text}"') +
40537                                     ' >{text}</span></a>'
40538                             )
40539                 );
40540             }
40541             
40542             switch (typeof(template)) {
40543                 case 'object' :
40544                     break;
40545                 case 'string' :
40546                     template = new Roo.Template(template);
40547                     break;
40548                 default :
40549                     break;
40550             }
40551             
40552             var el = template.overwrite(td, {"text": text});
40553             
40554             var inner = el.getElementsByTagName("span")[0];
40555             
40556             return {"el": el, "inner": inner};
40557             
40558     }
40559         
40560     
40561 });
40562
40563 /**
40564  * @class Roo.TabPanelItem
40565  * @extends Roo.util.Observable
40566  * Represents an individual item (tab plus body) in a TabPanel.
40567  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40568  * @param {String} id The id of this TabPanelItem
40569  * @param {String} text The text for the tab of this TabPanelItem
40570  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40571  */
40572 Roo.bootstrap.panel.TabItem = function(config){
40573     /**
40574      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40575      * @type Roo.TabPanel
40576      */
40577     this.tabPanel = config.panel;
40578     /**
40579      * The id for this TabPanelItem
40580      * @type String
40581      */
40582     this.id = config.id;
40583     /** @private */
40584     this.disabled = false;
40585     /** @private */
40586     this.text = config.text;
40587     /** @private */
40588     this.loaded = false;
40589     this.closable = config.closable;
40590
40591     /**
40592      * The body element for this TabPanelItem.
40593      * @type Roo.Element
40594      */
40595     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40596     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40597     this.bodyEl.setStyle("display", "block");
40598     this.bodyEl.setStyle("zoom", "1");
40599     //this.hideAction();
40600
40601     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40602     /** @private */
40603     this.el = Roo.get(els.el);
40604     this.inner = Roo.get(els.inner, true);
40605      this.textEl = Roo.bootstrap.version == 4 ?
40606         this.el : Roo.get(this.el.dom.firstChild, true);
40607
40608     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40609     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40610
40611     
40612 //    this.el.on("mousedown", this.onTabMouseDown, this);
40613     this.el.on("click", this.onTabClick, this);
40614     /** @private */
40615     if(config.closable){
40616         var c = Roo.get(els.close, true);
40617         c.dom.title = this.closeText;
40618         c.addClassOnOver("close-over");
40619         c.on("click", this.closeClick, this);
40620      }
40621
40622     this.addEvents({
40623          /**
40624          * @event activate
40625          * Fires when this tab becomes the active tab.
40626          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40627          * @param {Roo.TabPanelItem} this
40628          */
40629         "activate": true,
40630         /**
40631          * @event beforeclose
40632          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40633          * @param {Roo.TabPanelItem} this
40634          * @param {Object} e Set cancel to true on this object to cancel the close.
40635          */
40636         "beforeclose": true,
40637         /**
40638          * @event close
40639          * Fires when this tab is closed.
40640          * @param {Roo.TabPanelItem} this
40641          */
40642          "close": true,
40643         /**
40644          * @event deactivate
40645          * Fires when this tab is no longer the active tab.
40646          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40647          * @param {Roo.TabPanelItem} this
40648          */
40649          "deactivate" : true
40650     });
40651     this.hidden = false;
40652
40653     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40654 };
40655
40656 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40657            {
40658     purgeListeners : function(){
40659        Roo.util.Observable.prototype.purgeListeners.call(this);
40660        this.el.removeAllListeners();
40661     },
40662     /**
40663      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40664      */
40665     show : function(){
40666         this.status_node.addClass("active");
40667         this.showAction();
40668         if(Roo.isOpera){
40669             this.tabPanel.stripWrap.repaint();
40670         }
40671         this.fireEvent("activate", this.tabPanel, this);
40672     },
40673
40674     /**
40675      * Returns true if this tab is the active tab.
40676      * @return {Boolean}
40677      */
40678     isActive : function(){
40679         return this.tabPanel.getActiveTab() == this;
40680     },
40681
40682     /**
40683      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40684      */
40685     hide : function(){
40686         this.status_node.removeClass("active");
40687         this.hideAction();
40688         this.fireEvent("deactivate", this.tabPanel, this);
40689     },
40690
40691     hideAction : function(){
40692         this.bodyEl.hide();
40693         this.bodyEl.setStyle("position", "absolute");
40694         this.bodyEl.setLeft("-20000px");
40695         this.bodyEl.setTop("-20000px");
40696     },
40697
40698     showAction : function(){
40699         this.bodyEl.setStyle("position", "relative");
40700         this.bodyEl.setTop("");
40701         this.bodyEl.setLeft("");
40702         this.bodyEl.show();
40703     },
40704
40705     /**
40706      * Set the tooltip for the tab.
40707      * @param {String} tooltip The tab's tooltip
40708      */
40709     setTooltip : function(text){
40710         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40711             this.textEl.dom.qtip = text;
40712             this.textEl.dom.removeAttribute('title');
40713         }else{
40714             this.textEl.dom.title = text;
40715         }
40716     },
40717
40718     onTabClick : function(e){
40719         e.preventDefault();
40720         this.tabPanel.activate(this.id);
40721     },
40722
40723     onTabMouseDown : function(e){
40724         e.preventDefault();
40725         this.tabPanel.activate(this.id);
40726     },
40727 /*
40728     getWidth : function(){
40729         return this.inner.getWidth();
40730     },
40731
40732     setWidth : function(width){
40733         var iwidth = width - this.linode.getPadding("lr");
40734         this.inner.setWidth(iwidth);
40735         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40736         this.linode.setWidth(width);
40737     },
40738 */
40739     /**
40740      * Show or hide the tab
40741      * @param {Boolean} hidden True to hide or false to show.
40742      */
40743     setHidden : function(hidden){
40744         this.hidden = hidden;
40745         this.linode.setStyle("display", hidden ? "none" : "");
40746     },
40747
40748     /**
40749      * Returns true if this tab is "hidden"
40750      * @return {Boolean}
40751      */
40752     isHidden : function(){
40753         return this.hidden;
40754     },
40755
40756     /**
40757      * Returns the text for this tab
40758      * @return {String}
40759      */
40760     getText : function(){
40761         return this.text;
40762     },
40763     /*
40764     autoSize : function(){
40765         //this.el.beginMeasure();
40766         this.textEl.setWidth(1);
40767         /*
40768          *  #2804 [new] Tabs in Roojs
40769          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40770          */
40771         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40772         //this.el.endMeasure();
40773     //},
40774
40775     /**
40776      * Sets the text for the tab (Note: this also sets the tooltip text)
40777      * @param {String} text The tab's text and tooltip
40778      */
40779     setText : function(text){
40780         this.text = text;
40781         this.textEl.update(text);
40782         this.setTooltip(text);
40783         //if(!this.tabPanel.resizeTabs){
40784         //    this.autoSize();
40785         //}
40786     },
40787     /**
40788      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40789      */
40790     activate : function(){
40791         this.tabPanel.activate(this.id);
40792     },
40793
40794     /**
40795      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40796      */
40797     disable : function(){
40798         if(this.tabPanel.active != this){
40799             this.disabled = true;
40800             this.status_node.addClass("disabled");
40801         }
40802     },
40803
40804     /**
40805      * Enables this TabPanelItem if it was previously disabled.
40806      */
40807     enable : function(){
40808         this.disabled = false;
40809         this.status_node.removeClass("disabled");
40810     },
40811
40812     /**
40813      * Sets the content for this TabPanelItem.
40814      * @param {String} content The content
40815      * @param {Boolean} loadScripts true to look for and load scripts
40816      */
40817     setContent : function(content, loadScripts){
40818         this.bodyEl.update(content, loadScripts);
40819     },
40820
40821     /**
40822      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40823      * @return {Roo.UpdateManager} The UpdateManager
40824      */
40825     getUpdateManager : function(){
40826         return this.bodyEl.getUpdateManager();
40827     },
40828
40829     /**
40830      * Set a URL to be used to load the content for this TabPanelItem.
40831      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40832      * @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)
40833      * @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)
40834      * @return {Roo.UpdateManager} The UpdateManager
40835      */
40836     setUrl : function(url, params, loadOnce){
40837         if(this.refreshDelegate){
40838             this.un('activate', this.refreshDelegate);
40839         }
40840         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40841         this.on("activate", this.refreshDelegate);
40842         return this.bodyEl.getUpdateManager();
40843     },
40844
40845     /** @private */
40846     _handleRefresh : function(url, params, loadOnce){
40847         if(!loadOnce || !this.loaded){
40848             var updater = this.bodyEl.getUpdateManager();
40849             updater.update(url, params, this._setLoaded.createDelegate(this));
40850         }
40851     },
40852
40853     /**
40854      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40855      *   Will fail silently if the setUrl method has not been called.
40856      *   This does not activate the panel, just updates its content.
40857      */
40858     refresh : function(){
40859         if(this.refreshDelegate){
40860            this.loaded = false;
40861            this.refreshDelegate();
40862         }
40863     },
40864
40865     /** @private */
40866     _setLoaded : function(){
40867         this.loaded = true;
40868     },
40869
40870     /** @private */
40871     closeClick : function(e){
40872         var o = {};
40873         e.stopEvent();
40874         this.fireEvent("beforeclose", this, o);
40875         if(o.cancel !== true){
40876             this.tabPanel.removeTab(this.id);
40877         }
40878     },
40879     /**
40880      * The text displayed in the tooltip for the close icon.
40881      * @type String
40882      */
40883     closeText : "Close this tab"
40884 });
40885 /**
40886 *    This script refer to:
40887 *    Title: International Telephone Input
40888 *    Author: Jack O'Connor
40889 *    Code version:  v12.1.12
40890 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40891 **/
40892
40893 Roo.bootstrap.PhoneInputData = function() {
40894     var d = [
40895       [
40896         "Afghanistan (‫افغانستان‬‎)",
40897         "af",
40898         "93"
40899       ],
40900       [
40901         "Albania (Shqipëri)",
40902         "al",
40903         "355"
40904       ],
40905       [
40906         "Algeria (‫الجزائر‬‎)",
40907         "dz",
40908         "213"
40909       ],
40910       [
40911         "American Samoa",
40912         "as",
40913         "1684"
40914       ],
40915       [
40916         "Andorra",
40917         "ad",
40918         "376"
40919       ],
40920       [
40921         "Angola",
40922         "ao",
40923         "244"
40924       ],
40925       [
40926         "Anguilla",
40927         "ai",
40928         "1264"
40929       ],
40930       [
40931         "Antigua and Barbuda",
40932         "ag",
40933         "1268"
40934       ],
40935       [
40936         "Argentina",
40937         "ar",
40938         "54"
40939       ],
40940       [
40941         "Armenia (Հայաստան)",
40942         "am",
40943         "374"
40944       ],
40945       [
40946         "Aruba",
40947         "aw",
40948         "297"
40949       ],
40950       [
40951         "Australia",
40952         "au",
40953         "61",
40954         0
40955       ],
40956       [
40957         "Austria (Österreich)",
40958         "at",
40959         "43"
40960       ],
40961       [
40962         "Azerbaijan (Azərbaycan)",
40963         "az",
40964         "994"
40965       ],
40966       [
40967         "Bahamas",
40968         "bs",
40969         "1242"
40970       ],
40971       [
40972         "Bahrain (‫البحرين‬‎)",
40973         "bh",
40974         "973"
40975       ],
40976       [
40977         "Bangladesh (বাংলাদেশ)",
40978         "bd",
40979         "880"
40980       ],
40981       [
40982         "Barbados",
40983         "bb",
40984         "1246"
40985       ],
40986       [
40987         "Belarus (Беларусь)",
40988         "by",
40989         "375"
40990       ],
40991       [
40992         "Belgium (België)",
40993         "be",
40994         "32"
40995       ],
40996       [
40997         "Belize",
40998         "bz",
40999         "501"
41000       ],
41001       [
41002         "Benin (Bénin)",
41003         "bj",
41004         "229"
41005       ],
41006       [
41007         "Bermuda",
41008         "bm",
41009         "1441"
41010       ],
41011       [
41012         "Bhutan (འབྲུག)",
41013         "bt",
41014         "975"
41015       ],
41016       [
41017         "Bolivia",
41018         "bo",
41019         "591"
41020       ],
41021       [
41022         "Bosnia and Herzegovina (Босна и Херцеговина)",
41023         "ba",
41024         "387"
41025       ],
41026       [
41027         "Botswana",
41028         "bw",
41029         "267"
41030       ],
41031       [
41032         "Brazil (Brasil)",
41033         "br",
41034         "55"
41035       ],
41036       [
41037         "British Indian Ocean Territory",
41038         "io",
41039         "246"
41040       ],
41041       [
41042         "British Virgin Islands",
41043         "vg",
41044         "1284"
41045       ],
41046       [
41047         "Brunei",
41048         "bn",
41049         "673"
41050       ],
41051       [
41052         "Bulgaria (България)",
41053         "bg",
41054         "359"
41055       ],
41056       [
41057         "Burkina Faso",
41058         "bf",
41059         "226"
41060       ],
41061       [
41062         "Burundi (Uburundi)",
41063         "bi",
41064         "257"
41065       ],
41066       [
41067         "Cambodia (កម្ពុជា)",
41068         "kh",
41069         "855"
41070       ],
41071       [
41072         "Cameroon (Cameroun)",
41073         "cm",
41074         "237"
41075       ],
41076       [
41077         "Canada",
41078         "ca",
41079         "1",
41080         1,
41081         ["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"]
41082       ],
41083       [
41084         "Cape Verde (Kabu Verdi)",
41085         "cv",
41086         "238"
41087       ],
41088       [
41089         "Caribbean Netherlands",
41090         "bq",
41091         "599",
41092         1
41093       ],
41094       [
41095         "Cayman Islands",
41096         "ky",
41097         "1345"
41098       ],
41099       [
41100         "Central African Republic (République centrafricaine)",
41101         "cf",
41102         "236"
41103       ],
41104       [
41105         "Chad (Tchad)",
41106         "td",
41107         "235"
41108       ],
41109       [
41110         "Chile",
41111         "cl",
41112         "56"
41113       ],
41114       [
41115         "China (中国)",
41116         "cn",
41117         "86"
41118       ],
41119       [
41120         "Christmas Island",
41121         "cx",
41122         "61",
41123         2
41124       ],
41125       [
41126         "Cocos (Keeling) Islands",
41127         "cc",
41128         "61",
41129         1
41130       ],
41131       [
41132         "Colombia",
41133         "co",
41134         "57"
41135       ],
41136       [
41137         "Comoros (‫جزر القمر‬‎)",
41138         "km",
41139         "269"
41140       ],
41141       [
41142         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41143         "cd",
41144         "243"
41145       ],
41146       [
41147         "Congo (Republic) (Congo-Brazzaville)",
41148         "cg",
41149         "242"
41150       ],
41151       [
41152         "Cook Islands",
41153         "ck",
41154         "682"
41155       ],
41156       [
41157         "Costa Rica",
41158         "cr",
41159         "506"
41160       ],
41161       [
41162         "Côte d’Ivoire",
41163         "ci",
41164         "225"
41165       ],
41166       [
41167         "Croatia (Hrvatska)",
41168         "hr",
41169         "385"
41170       ],
41171       [
41172         "Cuba",
41173         "cu",
41174         "53"
41175       ],
41176       [
41177         "Curaçao",
41178         "cw",
41179         "599",
41180         0
41181       ],
41182       [
41183         "Cyprus (Κύπρος)",
41184         "cy",
41185         "357"
41186       ],
41187       [
41188         "Czech Republic (Česká republika)",
41189         "cz",
41190         "420"
41191       ],
41192       [
41193         "Denmark (Danmark)",
41194         "dk",
41195         "45"
41196       ],
41197       [
41198         "Djibouti",
41199         "dj",
41200         "253"
41201       ],
41202       [
41203         "Dominica",
41204         "dm",
41205         "1767"
41206       ],
41207       [
41208         "Dominican Republic (República Dominicana)",
41209         "do",
41210         "1",
41211         2,
41212         ["809", "829", "849"]
41213       ],
41214       [
41215         "Ecuador",
41216         "ec",
41217         "593"
41218       ],
41219       [
41220         "Egypt (‫مصر‬‎)",
41221         "eg",
41222         "20"
41223       ],
41224       [
41225         "El Salvador",
41226         "sv",
41227         "503"
41228       ],
41229       [
41230         "Equatorial Guinea (Guinea Ecuatorial)",
41231         "gq",
41232         "240"
41233       ],
41234       [
41235         "Eritrea",
41236         "er",
41237         "291"
41238       ],
41239       [
41240         "Estonia (Eesti)",
41241         "ee",
41242         "372"
41243       ],
41244       [
41245         "Ethiopia",
41246         "et",
41247         "251"
41248       ],
41249       [
41250         "Falkland Islands (Islas Malvinas)",
41251         "fk",
41252         "500"
41253       ],
41254       [
41255         "Faroe Islands (Føroyar)",
41256         "fo",
41257         "298"
41258       ],
41259       [
41260         "Fiji",
41261         "fj",
41262         "679"
41263       ],
41264       [
41265         "Finland (Suomi)",
41266         "fi",
41267         "358",
41268         0
41269       ],
41270       [
41271         "France",
41272         "fr",
41273         "33"
41274       ],
41275       [
41276         "French Guiana (Guyane française)",
41277         "gf",
41278         "594"
41279       ],
41280       [
41281         "French Polynesia (Polynésie française)",
41282         "pf",
41283         "689"
41284       ],
41285       [
41286         "Gabon",
41287         "ga",
41288         "241"
41289       ],
41290       [
41291         "Gambia",
41292         "gm",
41293         "220"
41294       ],
41295       [
41296         "Georgia (საქართველო)",
41297         "ge",
41298         "995"
41299       ],
41300       [
41301         "Germany (Deutschland)",
41302         "de",
41303         "49"
41304       ],
41305       [
41306         "Ghana (Gaana)",
41307         "gh",
41308         "233"
41309       ],
41310       [
41311         "Gibraltar",
41312         "gi",
41313         "350"
41314       ],
41315       [
41316         "Greece (Ελλάδα)",
41317         "gr",
41318         "30"
41319       ],
41320       [
41321         "Greenland (Kalaallit Nunaat)",
41322         "gl",
41323         "299"
41324       ],
41325       [
41326         "Grenada",
41327         "gd",
41328         "1473"
41329       ],
41330       [
41331         "Guadeloupe",
41332         "gp",
41333         "590",
41334         0
41335       ],
41336       [
41337         "Guam",
41338         "gu",
41339         "1671"
41340       ],
41341       [
41342         "Guatemala",
41343         "gt",
41344         "502"
41345       ],
41346       [
41347         "Guernsey",
41348         "gg",
41349         "44",
41350         1
41351       ],
41352       [
41353         "Guinea (Guinée)",
41354         "gn",
41355         "224"
41356       ],
41357       [
41358         "Guinea-Bissau (Guiné Bissau)",
41359         "gw",
41360         "245"
41361       ],
41362       [
41363         "Guyana",
41364         "gy",
41365         "592"
41366       ],
41367       [
41368         "Haiti",
41369         "ht",
41370         "509"
41371       ],
41372       [
41373         "Honduras",
41374         "hn",
41375         "504"
41376       ],
41377       [
41378         "Hong Kong (香港)",
41379         "hk",
41380         "852"
41381       ],
41382       [
41383         "Hungary (Magyarország)",
41384         "hu",
41385         "36"
41386       ],
41387       [
41388         "Iceland (Ísland)",
41389         "is",
41390         "354"
41391       ],
41392       [
41393         "India (भारत)",
41394         "in",
41395         "91"
41396       ],
41397       [
41398         "Indonesia",
41399         "id",
41400         "62"
41401       ],
41402       [
41403         "Iran (‫ایران‬‎)",
41404         "ir",
41405         "98"
41406       ],
41407       [
41408         "Iraq (‫العراق‬‎)",
41409         "iq",
41410         "964"
41411       ],
41412       [
41413         "Ireland",
41414         "ie",
41415         "353"
41416       ],
41417       [
41418         "Isle of Man",
41419         "im",
41420         "44",
41421         2
41422       ],
41423       [
41424         "Israel (‫ישראל‬‎)",
41425         "il",
41426         "972"
41427       ],
41428       [
41429         "Italy (Italia)",
41430         "it",
41431         "39",
41432         0
41433       ],
41434       [
41435         "Jamaica",
41436         "jm",
41437         "1876"
41438       ],
41439       [
41440         "Japan (日本)",
41441         "jp",
41442         "81"
41443       ],
41444       [
41445         "Jersey",
41446         "je",
41447         "44",
41448         3
41449       ],
41450       [
41451         "Jordan (‫الأردن‬‎)",
41452         "jo",
41453         "962"
41454       ],
41455       [
41456         "Kazakhstan (Казахстан)",
41457         "kz",
41458         "7",
41459         1
41460       ],
41461       [
41462         "Kenya",
41463         "ke",
41464         "254"
41465       ],
41466       [
41467         "Kiribati",
41468         "ki",
41469         "686"
41470       ],
41471       [
41472         "Kosovo",
41473         "xk",
41474         "383"
41475       ],
41476       [
41477         "Kuwait (‫الكويت‬‎)",
41478         "kw",
41479         "965"
41480       ],
41481       [
41482         "Kyrgyzstan (Кыргызстан)",
41483         "kg",
41484         "996"
41485       ],
41486       [
41487         "Laos (ລາວ)",
41488         "la",
41489         "856"
41490       ],
41491       [
41492         "Latvia (Latvija)",
41493         "lv",
41494         "371"
41495       ],
41496       [
41497         "Lebanon (‫لبنان‬‎)",
41498         "lb",
41499         "961"
41500       ],
41501       [
41502         "Lesotho",
41503         "ls",
41504         "266"
41505       ],
41506       [
41507         "Liberia",
41508         "lr",
41509         "231"
41510       ],
41511       [
41512         "Libya (‫ليبيا‬‎)",
41513         "ly",
41514         "218"
41515       ],
41516       [
41517         "Liechtenstein",
41518         "li",
41519         "423"
41520       ],
41521       [
41522         "Lithuania (Lietuva)",
41523         "lt",
41524         "370"
41525       ],
41526       [
41527         "Luxembourg",
41528         "lu",
41529         "352"
41530       ],
41531       [
41532         "Macau (澳門)",
41533         "mo",
41534         "853"
41535       ],
41536       [
41537         "Macedonia (FYROM) (Македонија)",
41538         "mk",
41539         "389"
41540       ],
41541       [
41542         "Madagascar (Madagasikara)",
41543         "mg",
41544         "261"
41545       ],
41546       [
41547         "Malawi",
41548         "mw",
41549         "265"
41550       ],
41551       [
41552         "Malaysia",
41553         "my",
41554         "60"
41555       ],
41556       [
41557         "Maldives",
41558         "mv",
41559         "960"
41560       ],
41561       [
41562         "Mali",
41563         "ml",
41564         "223"
41565       ],
41566       [
41567         "Malta",
41568         "mt",
41569         "356"
41570       ],
41571       [
41572         "Marshall Islands",
41573         "mh",
41574         "692"
41575       ],
41576       [
41577         "Martinique",
41578         "mq",
41579         "596"
41580       ],
41581       [
41582         "Mauritania (‫موريتانيا‬‎)",
41583         "mr",
41584         "222"
41585       ],
41586       [
41587         "Mauritius (Moris)",
41588         "mu",
41589         "230"
41590       ],
41591       [
41592         "Mayotte",
41593         "yt",
41594         "262",
41595         1
41596       ],
41597       [
41598         "Mexico (México)",
41599         "mx",
41600         "52"
41601       ],
41602       [
41603         "Micronesia",
41604         "fm",
41605         "691"
41606       ],
41607       [
41608         "Moldova (Republica Moldova)",
41609         "md",
41610         "373"
41611       ],
41612       [
41613         "Monaco",
41614         "mc",
41615         "377"
41616       ],
41617       [
41618         "Mongolia (Монгол)",
41619         "mn",
41620         "976"
41621       ],
41622       [
41623         "Montenegro (Crna Gora)",
41624         "me",
41625         "382"
41626       ],
41627       [
41628         "Montserrat",
41629         "ms",
41630         "1664"
41631       ],
41632       [
41633         "Morocco (‫المغرب‬‎)",
41634         "ma",
41635         "212",
41636         0
41637       ],
41638       [
41639         "Mozambique (Moçambique)",
41640         "mz",
41641         "258"
41642       ],
41643       [
41644         "Myanmar (Burma) (မြန်မာ)",
41645         "mm",
41646         "95"
41647       ],
41648       [
41649         "Namibia (Namibië)",
41650         "na",
41651         "264"
41652       ],
41653       [
41654         "Nauru",
41655         "nr",
41656         "674"
41657       ],
41658       [
41659         "Nepal (नेपाल)",
41660         "np",
41661         "977"
41662       ],
41663       [
41664         "Netherlands (Nederland)",
41665         "nl",
41666         "31"
41667       ],
41668       [
41669         "New Caledonia (Nouvelle-Calédonie)",
41670         "nc",
41671         "687"
41672       ],
41673       [
41674         "New Zealand",
41675         "nz",
41676         "64"
41677       ],
41678       [
41679         "Nicaragua",
41680         "ni",
41681         "505"
41682       ],
41683       [
41684         "Niger (Nijar)",
41685         "ne",
41686         "227"
41687       ],
41688       [
41689         "Nigeria",
41690         "ng",
41691         "234"
41692       ],
41693       [
41694         "Niue",
41695         "nu",
41696         "683"
41697       ],
41698       [
41699         "Norfolk Island",
41700         "nf",
41701         "672"
41702       ],
41703       [
41704         "North Korea (조선 민주주의 인민 공화국)",
41705         "kp",
41706         "850"
41707       ],
41708       [
41709         "Northern Mariana Islands",
41710         "mp",
41711         "1670"
41712       ],
41713       [
41714         "Norway (Norge)",
41715         "no",
41716         "47",
41717         0
41718       ],
41719       [
41720         "Oman (‫عُمان‬‎)",
41721         "om",
41722         "968"
41723       ],
41724       [
41725         "Pakistan (‫پاکستان‬‎)",
41726         "pk",
41727         "92"
41728       ],
41729       [
41730         "Palau",
41731         "pw",
41732         "680"
41733       ],
41734       [
41735         "Palestine (‫فلسطين‬‎)",
41736         "ps",
41737         "970"
41738       ],
41739       [
41740         "Panama (Panamá)",
41741         "pa",
41742         "507"
41743       ],
41744       [
41745         "Papua New Guinea",
41746         "pg",
41747         "675"
41748       ],
41749       [
41750         "Paraguay",
41751         "py",
41752         "595"
41753       ],
41754       [
41755         "Peru (Perú)",
41756         "pe",
41757         "51"
41758       ],
41759       [
41760         "Philippines",
41761         "ph",
41762         "63"
41763       ],
41764       [
41765         "Poland (Polska)",
41766         "pl",
41767         "48"
41768       ],
41769       [
41770         "Portugal",
41771         "pt",
41772         "351"
41773       ],
41774       [
41775         "Puerto Rico",
41776         "pr",
41777         "1",
41778         3,
41779         ["787", "939"]
41780       ],
41781       [
41782         "Qatar (‫قطر‬‎)",
41783         "qa",
41784         "974"
41785       ],
41786       [
41787         "Réunion (La Réunion)",
41788         "re",
41789         "262",
41790         0
41791       ],
41792       [
41793         "Romania (România)",
41794         "ro",
41795         "40"
41796       ],
41797       [
41798         "Russia (Россия)",
41799         "ru",
41800         "7",
41801         0
41802       ],
41803       [
41804         "Rwanda",
41805         "rw",
41806         "250"
41807       ],
41808       [
41809         "Saint Barthélemy",
41810         "bl",
41811         "590",
41812         1
41813       ],
41814       [
41815         "Saint Helena",
41816         "sh",
41817         "290"
41818       ],
41819       [
41820         "Saint Kitts and Nevis",
41821         "kn",
41822         "1869"
41823       ],
41824       [
41825         "Saint Lucia",
41826         "lc",
41827         "1758"
41828       ],
41829       [
41830         "Saint Martin (Saint-Martin (partie française))",
41831         "mf",
41832         "590",
41833         2
41834       ],
41835       [
41836         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41837         "pm",
41838         "508"
41839       ],
41840       [
41841         "Saint Vincent and the Grenadines",
41842         "vc",
41843         "1784"
41844       ],
41845       [
41846         "Samoa",
41847         "ws",
41848         "685"
41849       ],
41850       [
41851         "San Marino",
41852         "sm",
41853         "378"
41854       ],
41855       [
41856         "São Tomé and Príncipe (São Tomé e Príncipe)",
41857         "st",
41858         "239"
41859       ],
41860       [
41861         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41862         "sa",
41863         "966"
41864       ],
41865       [
41866         "Senegal (Sénégal)",
41867         "sn",
41868         "221"
41869       ],
41870       [
41871         "Serbia (Србија)",
41872         "rs",
41873         "381"
41874       ],
41875       [
41876         "Seychelles",
41877         "sc",
41878         "248"
41879       ],
41880       [
41881         "Sierra Leone",
41882         "sl",
41883         "232"
41884       ],
41885       [
41886         "Singapore",
41887         "sg",
41888         "65"
41889       ],
41890       [
41891         "Sint Maarten",
41892         "sx",
41893         "1721"
41894       ],
41895       [
41896         "Slovakia (Slovensko)",
41897         "sk",
41898         "421"
41899       ],
41900       [
41901         "Slovenia (Slovenija)",
41902         "si",
41903         "386"
41904       ],
41905       [
41906         "Solomon Islands",
41907         "sb",
41908         "677"
41909       ],
41910       [
41911         "Somalia (Soomaaliya)",
41912         "so",
41913         "252"
41914       ],
41915       [
41916         "South Africa",
41917         "za",
41918         "27"
41919       ],
41920       [
41921         "South Korea (대한민국)",
41922         "kr",
41923         "82"
41924       ],
41925       [
41926         "South Sudan (‫جنوب السودان‬‎)",
41927         "ss",
41928         "211"
41929       ],
41930       [
41931         "Spain (España)",
41932         "es",
41933         "34"
41934       ],
41935       [
41936         "Sri Lanka (ශ්‍රී ලංකාව)",
41937         "lk",
41938         "94"
41939       ],
41940       [
41941         "Sudan (‫السودان‬‎)",
41942         "sd",
41943         "249"
41944       ],
41945       [
41946         "Suriname",
41947         "sr",
41948         "597"
41949       ],
41950       [
41951         "Svalbard and Jan Mayen",
41952         "sj",
41953         "47",
41954         1
41955       ],
41956       [
41957         "Swaziland",
41958         "sz",
41959         "268"
41960       ],
41961       [
41962         "Sweden (Sverige)",
41963         "se",
41964         "46"
41965       ],
41966       [
41967         "Switzerland (Schweiz)",
41968         "ch",
41969         "41"
41970       ],
41971       [
41972         "Syria (‫سوريا‬‎)",
41973         "sy",
41974         "963"
41975       ],
41976       [
41977         "Taiwan (台灣)",
41978         "tw",
41979         "886"
41980       ],
41981       [
41982         "Tajikistan",
41983         "tj",
41984         "992"
41985       ],
41986       [
41987         "Tanzania",
41988         "tz",
41989         "255"
41990       ],
41991       [
41992         "Thailand (ไทย)",
41993         "th",
41994         "66"
41995       ],
41996       [
41997         "Timor-Leste",
41998         "tl",
41999         "670"
42000       ],
42001       [
42002         "Togo",
42003         "tg",
42004         "228"
42005       ],
42006       [
42007         "Tokelau",
42008         "tk",
42009         "690"
42010       ],
42011       [
42012         "Tonga",
42013         "to",
42014         "676"
42015       ],
42016       [
42017         "Trinidad and Tobago",
42018         "tt",
42019         "1868"
42020       ],
42021       [
42022         "Tunisia (‫تونس‬‎)",
42023         "tn",
42024         "216"
42025       ],
42026       [
42027         "Turkey (Türkiye)",
42028         "tr",
42029         "90"
42030       ],
42031       [
42032         "Turkmenistan",
42033         "tm",
42034         "993"
42035       ],
42036       [
42037         "Turks and Caicos Islands",
42038         "tc",
42039         "1649"
42040       ],
42041       [
42042         "Tuvalu",
42043         "tv",
42044         "688"
42045       ],
42046       [
42047         "U.S. Virgin Islands",
42048         "vi",
42049         "1340"
42050       ],
42051       [
42052         "Uganda",
42053         "ug",
42054         "256"
42055       ],
42056       [
42057         "Ukraine (Україна)",
42058         "ua",
42059         "380"
42060       ],
42061       [
42062         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42063         "ae",
42064         "971"
42065       ],
42066       [
42067         "United Kingdom",
42068         "gb",
42069         "44",
42070         0
42071       ],
42072       [
42073         "United States",
42074         "us",
42075         "1",
42076         0
42077       ],
42078       [
42079         "Uruguay",
42080         "uy",
42081         "598"
42082       ],
42083       [
42084         "Uzbekistan (Oʻzbekiston)",
42085         "uz",
42086         "998"
42087       ],
42088       [
42089         "Vanuatu",
42090         "vu",
42091         "678"
42092       ],
42093       [
42094         "Vatican City (Città del Vaticano)",
42095         "va",
42096         "39",
42097         1
42098       ],
42099       [
42100         "Venezuela",
42101         "ve",
42102         "58"
42103       ],
42104       [
42105         "Vietnam (Việt Nam)",
42106         "vn",
42107         "84"
42108       ],
42109       [
42110         "Wallis and Futuna (Wallis-et-Futuna)",
42111         "wf",
42112         "681"
42113       ],
42114       [
42115         "Western Sahara (‫الصحراء الغربية‬‎)",
42116         "eh",
42117         "212",
42118         1
42119       ],
42120       [
42121         "Yemen (‫اليمن‬‎)",
42122         "ye",
42123         "967"
42124       ],
42125       [
42126         "Zambia",
42127         "zm",
42128         "260"
42129       ],
42130       [
42131         "Zimbabwe",
42132         "zw",
42133         "263"
42134       ],
42135       [
42136         "Åland Islands",
42137         "ax",
42138         "358",
42139         1
42140       ]
42141   ];
42142   
42143   return d;
42144 }/**
42145 *    This script refer to:
42146 *    Title: International Telephone Input
42147 *    Author: Jack O'Connor
42148 *    Code version:  v12.1.12
42149 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42150 **/
42151
42152 /**
42153  * @class Roo.bootstrap.PhoneInput
42154  * @extends Roo.bootstrap.TriggerField
42155  * An input with International dial-code selection
42156  
42157  * @cfg {String} defaultDialCode default '+852'
42158  * @cfg {Array} preferedCountries default []
42159   
42160  * @constructor
42161  * Create a new PhoneInput.
42162  * @param {Object} config Configuration options
42163  */
42164
42165 Roo.bootstrap.PhoneInput = function(config) {
42166     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42167 };
42168
42169 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42170         
42171         listWidth: undefined,
42172         
42173         selectedClass: 'active',
42174         
42175         invalidClass : "has-warning",
42176         
42177         validClass: 'has-success',
42178         
42179         allowed: '0123456789',
42180         
42181         max_length: 15,
42182         
42183         /**
42184          * @cfg {String} defaultDialCode The default dial code when initializing the input
42185          */
42186         defaultDialCode: '+852',
42187         
42188         /**
42189          * @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
42190          */
42191         preferedCountries: false,
42192         
42193         getAutoCreate : function()
42194         {
42195             var data = Roo.bootstrap.PhoneInputData();
42196             var align = this.labelAlign || this.parentLabelAlign();
42197             var id = Roo.id();
42198             
42199             this.allCountries = [];
42200             this.dialCodeMapping = [];
42201             
42202             for (var i = 0; i < data.length; i++) {
42203               var c = data[i];
42204               this.allCountries[i] = {
42205                 name: c[0],
42206                 iso2: c[1],
42207                 dialCode: c[2],
42208                 priority: c[3] || 0,
42209                 areaCodes: c[4] || null
42210               };
42211               this.dialCodeMapping[c[2]] = {
42212                   name: c[0],
42213                   iso2: c[1],
42214                   priority: c[3] || 0,
42215                   areaCodes: c[4] || null
42216               };
42217             }
42218             
42219             var cfg = {
42220                 cls: 'form-group',
42221                 cn: []
42222             };
42223             
42224             var input =  {
42225                 tag: 'input',
42226                 id : id,
42227                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42228                 maxlength: this.max_length,
42229                 cls : 'form-control tel-input',
42230                 autocomplete: 'new-password'
42231             };
42232             
42233             var hiddenInput = {
42234                 tag: 'input',
42235                 type: 'hidden',
42236                 cls: 'hidden-tel-input'
42237             };
42238             
42239             if (this.name) {
42240                 hiddenInput.name = this.name;
42241             }
42242             
42243             if (this.disabled) {
42244                 input.disabled = true;
42245             }
42246             
42247             var flag_container = {
42248                 tag: 'div',
42249                 cls: 'flag-box',
42250                 cn: [
42251                     {
42252                         tag: 'div',
42253                         cls: 'flag'
42254                     },
42255                     {
42256                         tag: 'div',
42257                         cls: 'caret'
42258                     }
42259                 ]
42260             };
42261             
42262             var box = {
42263                 tag: 'div',
42264                 cls: this.hasFeedback ? 'has-feedback' : '',
42265                 cn: [
42266                     hiddenInput,
42267                     input,
42268                     {
42269                         tag: 'input',
42270                         cls: 'dial-code-holder',
42271                         disabled: true
42272                     }
42273                 ]
42274             };
42275             
42276             var container = {
42277                 cls: 'roo-select2-container input-group',
42278                 cn: [
42279                     flag_container,
42280                     box
42281                 ]
42282             };
42283             
42284             if (this.fieldLabel.length) {
42285                 var indicator = {
42286                     tag: 'i',
42287                     tooltip: 'This field is required'
42288                 };
42289                 
42290                 var label = {
42291                     tag: 'label',
42292                     'for':  id,
42293                     cls: 'control-label',
42294                     cn: []
42295                 };
42296                 
42297                 var label_text = {
42298                     tag: 'span',
42299                     html: this.fieldLabel
42300                 };
42301                 
42302                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42303                 label.cn = [
42304                     indicator,
42305                     label_text
42306                 ];
42307                 
42308                 if(this.indicatorpos == 'right') {
42309                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42310                     label.cn = [
42311                         label_text,
42312                         indicator
42313                     ];
42314                 }
42315                 
42316                 if(align == 'left') {
42317                     container = {
42318                         tag: 'div',
42319                         cn: [
42320                             container
42321                         ]
42322                     };
42323                     
42324                     if(this.labelWidth > 12){
42325                         label.style = "width: " + this.labelWidth + 'px';
42326                     }
42327                     if(this.labelWidth < 13 && this.labelmd == 0){
42328                         this.labelmd = this.labelWidth;
42329                     }
42330                     if(this.labellg > 0){
42331                         label.cls += ' col-lg-' + this.labellg;
42332                         input.cls += ' col-lg-' + (12 - this.labellg);
42333                     }
42334                     if(this.labelmd > 0){
42335                         label.cls += ' col-md-' + this.labelmd;
42336                         container.cls += ' col-md-' + (12 - this.labelmd);
42337                     }
42338                     if(this.labelsm > 0){
42339                         label.cls += ' col-sm-' + this.labelsm;
42340                         container.cls += ' col-sm-' + (12 - this.labelsm);
42341                     }
42342                     if(this.labelxs > 0){
42343                         label.cls += ' col-xs-' + this.labelxs;
42344                         container.cls += ' col-xs-' + (12 - this.labelxs);
42345                     }
42346                 }
42347             }
42348             
42349             cfg.cn = [
42350                 label,
42351                 container
42352             ];
42353             
42354             var settings = this;
42355             
42356             ['xs','sm','md','lg'].map(function(size){
42357                 if (settings[size]) {
42358                     cfg.cls += ' col-' + size + '-' + settings[size];
42359                 }
42360             });
42361             
42362             this.store = new Roo.data.Store({
42363                 proxy : new Roo.data.MemoryProxy({}),
42364                 reader : new Roo.data.JsonReader({
42365                     fields : [
42366                         {
42367                             'name' : 'name',
42368                             'type' : 'string'
42369                         },
42370                         {
42371                             'name' : 'iso2',
42372                             'type' : 'string'
42373                         },
42374                         {
42375                             'name' : 'dialCode',
42376                             'type' : 'string'
42377                         },
42378                         {
42379                             'name' : 'priority',
42380                             'type' : 'string'
42381                         },
42382                         {
42383                             'name' : 'areaCodes',
42384                             'type' : 'string'
42385                         }
42386                     ]
42387                 })
42388             });
42389             
42390             if(!this.preferedCountries) {
42391                 this.preferedCountries = [
42392                     'hk',
42393                     'gb',
42394                     'us'
42395                 ];
42396             }
42397             
42398             var p = this.preferedCountries.reverse();
42399             
42400             if(p) {
42401                 for (var i = 0; i < p.length; i++) {
42402                     for (var j = 0; j < this.allCountries.length; j++) {
42403                         if(this.allCountries[j].iso2 == p[i]) {
42404                             var t = this.allCountries[j];
42405                             this.allCountries.splice(j,1);
42406                             this.allCountries.unshift(t);
42407                         }
42408                     } 
42409                 }
42410             }
42411             
42412             this.store.proxy.data = {
42413                 success: true,
42414                 data: this.allCountries
42415             };
42416             
42417             return cfg;
42418         },
42419         
42420         initEvents : function()
42421         {
42422             this.createList();
42423             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42424             
42425             this.indicator = this.indicatorEl();
42426             this.flag = this.flagEl();
42427             this.dialCodeHolder = this.dialCodeHolderEl();
42428             
42429             this.trigger = this.el.select('div.flag-box',true).first();
42430             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42431             
42432             var _this = this;
42433             
42434             (function(){
42435                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42436                 _this.list.setWidth(lw);
42437             }).defer(100);
42438             
42439             this.list.on('mouseover', this.onViewOver, this);
42440             this.list.on('mousemove', this.onViewMove, this);
42441             this.inputEl().on("keyup", this.onKeyUp, this);
42442             this.inputEl().on("keypress", this.onKeyPress, this);
42443             
42444             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42445
42446             this.view = new Roo.View(this.list, this.tpl, {
42447                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42448             });
42449             
42450             this.view.on('click', this.onViewClick, this);
42451             this.setValue(this.defaultDialCode);
42452         },
42453         
42454         onTriggerClick : function(e)
42455         {
42456             Roo.log('trigger click');
42457             if(this.disabled){
42458                 return;
42459             }
42460             
42461             if(this.isExpanded()){
42462                 this.collapse();
42463                 this.hasFocus = false;
42464             }else {
42465                 this.store.load({});
42466                 this.hasFocus = true;
42467                 this.expand();
42468             }
42469         },
42470         
42471         isExpanded : function()
42472         {
42473             return this.list.isVisible();
42474         },
42475         
42476         collapse : function()
42477         {
42478             if(!this.isExpanded()){
42479                 return;
42480             }
42481             this.list.hide();
42482             Roo.get(document).un('mousedown', this.collapseIf, this);
42483             Roo.get(document).un('mousewheel', this.collapseIf, this);
42484             this.fireEvent('collapse', this);
42485             this.validate();
42486         },
42487         
42488         expand : function()
42489         {
42490             Roo.log('expand');
42491
42492             if(this.isExpanded() || !this.hasFocus){
42493                 return;
42494             }
42495             
42496             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42497             this.list.setWidth(lw);
42498             
42499             this.list.show();
42500             this.restrictHeight();
42501             
42502             Roo.get(document).on('mousedown', this.collapseIf, this);
42503             Roo.get(document).on('mousewheel', this.collapseIf, this);
42504             
42505             this.fireEvent('expand', this);
42506         },
42507         
42508         restrictHeight : function()
42509         {
42510             this.list.alignTo(this.inputEl(), this.listAlign);
42511             this.list.alignTo(this.inputEl(), this.listAlign);
42512         },
42513         
42514         onViewOver : function(e, t)
42515         {
42516             if(this.inKeyMode){
42517                 return;
42518             }
42519             var item = this.view.findItemFromChild(t);
42520             
42521             if(item){
42522                 var index = this.view.indexOf(item);
42523                 this.select(index, false);
42524             }
42525         },
42526
42527         // private
42528         onViewClick : function(view, doFocus, el, e)
42529         {
42530             var index = this.view.getSelectedIndexes()[0];
42531             
42532             var r = this.store.getAt(index);
42533             
42534             if(r){
42535                 this.onSelect(r, index);
42536             }
42537             if(doFocus !== false && !this.blockFocus){
42538                 this.inputEl().focus();
42539             }
42540         },
42541         
42542         onViewMove : function(e, t)
42543         {
42544             this.inKeyMode = false;
42545         },
42546         
42547         select : function(index, scrollIntoView)
42548         {
42549             this.selectedIndex = index;
42550             this.view.select(index);
42551             if(scrollIntoView !== false){
42552                 var el = this.view.getNode(index);
42553                 if(el){
42554                     this.list.scrollChildIntoView(el, false);
42555                 }
42556             }
42557         },
42558         
42559         createList : function()
42560         {
42561             this.list = Roo.get(document.body).createChild({
42562                 tag: 'ul',
42563                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42564                 style: 'display:none'
42565             });
42566             
42567             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42568         },
42569         
42570         collapseIf : function(e)
42571         {
42572             var in_combo  = e.within(this.el);
42573             var in_list =  e.within(this.list);
42574             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42575             
42576             if (in_combo || in_list || is_list) {
42577                 return;
42578             }
42579             this.collapse();
42580         },
42581         
42582         onSelect : function(record, index)
42583         {
42584             if(this.fireEvent('beforeselect', this, record, index) !== false){
42585                 
42586                 this.setFlagClass(record.data.iso2);
42587                 this.setDialCode(record.data.dialCode);
42588                 this.hasFocus = false;
42589                 this.collapse();
42590                 this.fireEvent('select', this, record, index);
42591             }
42592         },
42593         
42594         flagEl : function()
42595         {
42596             var flag = this.el.select('div.flag',true).first();
42597             if(!flag){
42598                 return false;
42599             }
42600             return flag;
42601         },
42602         
42603         dialCodeHolderEl : function()
42604         {
42605             var d = this.el.select('input.dial-code-holder',true).first();
42606             if(!d){
42607                 return false;
42608             }
42609             return d;
42610         },
42611         
42612         setDialCode : function(v)
42613         {
42614             this.dialCodeHolder.dom.value = '+'+v;
42615         },
42616         
42617         setFlagClass : function(n)
42618         {
42619             this.flag.dom.className = 'flag '+n;
42620         },
42621         
42622         getValue : function()
42623         {
42624             var v = this.inputEl().getValue();
42625             if(this.dialCodeHolder) {
42626                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42627             }
42628             return v;
42629         },
42630         
42631         setValue : function(v)
42632         {
42633             var d = this.getDialCode(v);
42634             
42635             //invalid dial code
42636             if(v.length == 0 || !d || d.length == 0) {
42637                 if(this.rendered){
42638                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42639                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42640                 }
42641                 return;
42642             }
42643             
42644             //valid dial code
42645             this.setFlagClass(this.dialCodeMapping[d].iso2);
42646             this.setDialCode(d);
42647             this.inputEl().dom.value = v.replace('+'+d,'');
42648             this.hiddenEl().dom.value = this.getValue();
42649             
42650             this.validate();
42651         },
42652         
42653         getDialCode : function(v)
42654         {
42655             v = v ||  '';
42656             
42657             if (v.length == 0) {
42658                 return this.dialCodeHolder.dom.value;
42659             }
42660             
42661             var dialCode = "";
42662             if (v.charAt(0) != "+") {
42663                 return false;
42664             }
42665             var numericChars = "";
42666             for (var i = 1; i < v.length; i++) {
42667               var c = v.charAt(i);
42668               if (!isNaN(c)) {
42669                 numericChars += c;
42670                 if (this.dialCodeMapping[numericChars]) {
42671                   dialCode = v.substr(1, i);
42672                 }
42673                 if (numericChars.length == 4) {
42674                   break;
42675                 }
42676               }
42677             }
42678             return dialCode;
42679         },
42680         
42681         reset : function()
42682         {
42683             this.setValue(this.defaultDialCode);
42684             this.validate();
42685         },
42686         
42687         hiddenEl : function()
42688         {
42689             return this.el.select('input.hidden-tel-input',true).first();
42690         },
42691         
42692         // after setting val
42693         onKeyUp : function(e){
42694             this.setValue(this.getValue());
42695         },
42696         
42697         onKeyPress : function(e){
42698             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42699                 e.stopEvent();
42700             }
42701         }
42702         
42703 });
42704 /**
42705  * @class Roo.bootstrap.MoneyField
42706  * @extends Roo.bootstrap.ComboBox
42707  * Bootstrap MoneyField class
42708  * 
42709  * @constructor
42710  * Create a new MoneyField.
42711  * @param {Object} config Configuration options
42712  */
42713
42714 Roo.bootstrap.MoneyField = function(config) {
42715     
42716     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42717     
42718 };
42719
42720 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42721     
42722     /**
42723      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42724      */
42725     allowDecimals : true,
42726     /**
42727      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42728      */
42729     decimalSeparator : ".",
42730     /**
42731      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42732      */
42733     decimalPrecision : 0,
42734     /**
42735      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42736      */
42737     allowNegative : true,
42738     /**
42739      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42740      */
42741     allowZero: true,
42742     /**
42743      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42744      */
42745     minValue : Number.NEGATIVE_INFINITY,
42746     /**
42747      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42748      */
42749     maxValue : Number.MAX_VALUE,
42750     /**
42751      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42752      */
42753     minText : "The minimum value for this field is {0}",
42754     /**
42755      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42756      */
42757     maxText : "The maximum value for this field is {0}",
42758     /**
42759      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42760      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42761      */
42762     nanText : "{0} is not a valid number",
42763     /**
42764      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42765      */
42766     castInt : true,
42767     /**
42768      * @cfg {String} defaults currency of the MoneyField
42769      * value should be in lkey
42770      */
42771     defaultCurrency : false,
42772     /**
42773      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42774      */
42775     thousandsDelimiter : false,
42776     /**
42777      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42778      */
42779     max_length: false,
42780     
42781     inputlg : 9,
42782     inputmd : 9,
42783     inputsm : 9,
42784     inputxs : 6,
42785     
42786     store : false,
42787     
42788     getAutoCreate : function()
42789     {
42790         var align = this.labelAlign || this.parentLabelAlign();
42791         
42792         var id = Roo.id();
42793
42794         var cfg = {
42795             cls: 'form-group',
42796             cn: []
42797         };
42798
42799         var input =  {
42800             tag: 'input',
42801             id : id,
42802             cls : 'form-control roo-money-amount-input',
42803             autocomplete: 'new-password'
42804         };
42805         
42806         var hiddenInput = {
42807             tag: 'input',
42808             type: 'hidden',
42809             id: Roo.id(),
42810             cls: 'hidden-number-input'
42811         };
42812         
42813         if(this.max_length) {
42814             input.maxlength = this.max_length; 
42815         }
42816         
42817         if (this.name) {
42818             hiddenInput.name = this.name;
42819         }
42820
42821         if (this.disabled) {
42822             input.disabled = true;
42823         }
42824
42825         var clg = 12 - this.inputlg;
42826         var cmd = 12 - this.inputmd;
42827         var csm = 12 - this.inputsm;
42828         var cxs = 12 - this.inputxs;
42829         
42830         var container = {
42831             tag : 'div',
42832             cls : 'row roo-money-field',
42833             cn : [
42834                 {
42835                     tag : 'div',
42836                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42837                     cn : [
42838                         {
42839                             tag : 'div',
42840                             cls: 'roo-select2-container input-group',
42841                             cn: [
42842                                 {
42843                                     tag : 'input',
42844                                     cls : 'form-control roo-money-currency-input',
42845                                     autocomplete: 'new-password',
42846                                     readOnly : 1,
42847                                     name : this.currencyName
42848                                 },
42849                                 {
42850                                     tag :'span',
42851                                     cls : 'input-group-addon',
42852                                     cn : [
42853                                         {
42854                                             tag: 'span',
42855                                             cls: 'caret'
42856                                         }
42857                                     ]
42858                                 }
42859                             ]
42860                         }
42861                     ]
42862                 },
42863                 {
42864                     tag : 'div',
42865                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42866                     cn : [
42867                         {
42868                             tag: 'div',
42869                             cls: this.hasFeedback ? 'has-feedback' : '',
42870                             cn: [
42871                                 input
42872                             ]
42873                         }
42874                     ]
42875                 }
42876             ]
42877             
42878         };
42879         
42880         if (this.fieldLabel.length) {
42881             var indicator = {
42882                 tag: 'i',
42883                 tooltip: 'This field is required'
42884             };
42885
42886             var label = {
42887                 tag: 'label',
42888                 'for':  id,
42889                 cls: 'control-label',
42890                 cn: []
42891             };
42892
42893             var label_text = {
42894                 tag: 'span',
42895                 html: this.fieldLabel
42896             };
42897
42898             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42899             label.cn = [
42900                 indicator,
42901                 label_text
42902             ];
42903
42904             if(this.indicatorpos == 'right') {
42905                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42906                 label.cn = [
42907                     label_text,
42908                     indicator
42909                 ];
42910             }
42911
42912             if(align == 'left') {
42913                 container = {
42914                     tag: 'div',
42915                     cn: [
42916                         container
42917                     ]
42918                 };
42919
42920                 if(this.labelWidth > 12){
42921                     label.style = "width: " + this.labelWidth + 'px';
42922                 }
42923                 if(this.labelWidth < 13 && this.labelmd == 0){
42924                     this.labelmd = this.labelWidth;
42925                 }
42926                 if(this.labellg > 0){
42927                     label.cls += ' col-lg-' + this.labellg;
42928                     input.cls += ' col-lg-' + (12 - this.labellg);
42929                 }
42930                 if(this.labelmd > 0){
42931                     label.cls += ' col-md-' + this.labelmd;
42932                     container.cls += ' col-md-' + (12 - this.labelmd);
42933                 }
42934                 if(this.labelsm > 0){
42935                     label.cls += ' col-sm-' + this.labelsm;
42936                     container.cls += ' col-sm-' + (12 - this.labelsm);
42937                 }
42938                 if(this.labelxs > 0){
42939                     label.cls += ' col-xs-' + this.labelxs;
42940                     container.cls += ' col-xs-' + (12 - this.labelxs);
42941                 }
42942             }
42943         }
42944
42945         cfg.cn = [
42946             label,
42947             container,
42948             hiddenInput
42949         ];
42950         
42951         var settings = this;
42952
42953         ['xs','sm','md','lg'].map(function(size){
42954             if (settings[size]) {
42955                 cfg.cls += ' col-' + size + '-' + settings[size];
42956             }
42957         });
42958         
42959         return cfg;
42960     },
42961     
42962     initEvents : function()
42963     {
42964         this.indicator = this.indicatorEl();
42965         
42966         this.initCurrencyEvent();
42967         
42968         this.initNumberEvent();
42969     },
42970     
42971     initCurrencyEvent : function()
42972     {
42973         if (!this.store) {
42974             throw "can not find store for combo";
42975         }
42976         
42977         this.store = Roo.factory(this.store, Roo.data);
42978         this.store.parent = this;
42979         
42980         this.createList();
42981         
42982         this.triggerEl = this.el.select('.input-group-addon', true).first();
42983         
42984         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42985         
42986         var _this = this;
42987         
42988         (function(){
42989             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42990             _this.list.setWidth(lw);
42991         }).defer(100);
42992         
42993         this.list.on('mouseover', this.onViewOver, this);
42994         this.list.on('mousemove', this.onViewMove, this);
42995         this.list.on('scroll', this.onViewScroll, this);
42996         
42997         if(!this.tpl){
42998             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42999         }
43000         
43001         this.view = new Roo.View(this.list, this.tpl, {
43002             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43003         });
43004         
43005         this.view.on('click', this.onViewClick, this);
43006         
43007         this.store.on('beforeload', this.onBeforeLoad, this);
43008         this.store.on('load', this.onLoad, this);
43009         this.store.on('loadexception', this.onLoadException, this);
43010         
43011         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43012             "up" : function(e){
43013                 this.inKeyMode = true;
43014                 this.selectPrev();
43015             },
43016
43017             "down" : function(e){
43018                 if(!this.isExpanded()){
43019                     this.onTriggerClick();
43020                 }else{
43021                     this.inKeyMode = true;
43022                     this.selectNext();
43023                 }
43024             },
43025
43026             "enter" : function(e){
43027                 this.collapse();
43028                 
43029                 if(this.fireEvent("specialkey", this, e)){
43030                     this.onViewClick(false);
43031                 }
43032                 
43033                 return true;
43034             },
43035
43036             "esc" : function(e){
43037                 this.collapse();
43038             },
43039
43040             "tab" : function(e){
43041                 this.collapse();
43042                 
43043                 if(this.fireEvent("specialkey", this, e)){
43044                     this.onViewClick(false);
43045                 }
43046                 
43047                 return true;
43048             },
43049
43050             scope : this,
43051
43052             doRelay : function(foo, bar, hname){
43053                 if(hname == 'down' || this.scope.isExpanded()){
43054                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43055                 }
43056                 return true;
43057             },
43058
43059             forceKeyDown: true
43060         });
43061         
43062         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43063         
43064     },
43065     
43066     initNumberEvent : function(e)
43067     {
43068         this.inputEl().on("keydown" , this.fireKey,  this);
43069         this.inputEl().on("focus", this.onFocus,  this);
43070         this.inputEl().on("blur", this.onBlur,  this);
43071         
43072         this.inputEl().relayEvent('keyup', this);
43073         
43074         if(this.indicator){
43075             this.indicator.addClass('invisible');
43076         }
43077  
43078         this.originalValue = this.getValue();
43079         
43080         if(this.validationEvent == 'keyup'){
43081             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43082             this.inputEl().on('keyup', this.filterValidation, this);
43083         }
43084         else if(this.validationEvent !== false){
43085             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43086         }
43087         
43088         if(this.selectOnFocus){
43089             this.on("focus", this.preFocus, this);
43090             
43091         }
43092         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43093             this.inputEl().on("keypress", this.filterKeys, this);
43094         } else {
43095             this.inputEl().relayEvent('keypress', this);
43096         }
43097         
43098         var allowed = "0123456789";
43099         
43100         if(this.allowDecimals){
43101             allowed += this.decimalSeparator;
43102         }
43103         
43104         if(this.allowNegative){
43105             allowed += "-";
43106         }
43107         
43108         if(this.thousandsDelimiter) {
43109             allowed += ",";
43110         }
43111         
43112         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43113         
43114         var keyPress = function(e){
43115             
43116             var k = e.getKey();
43117             
43118             var c = e.getCharCode();
43119             
43120             if(
43121                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43122                     allowed.indexOf(String.fromCharCode(c)) === -1
43123             ){
43124                 e.stopEvent();
43125                 return;
43126             }
43127             
43128             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43129                 return;
43130             }
43131             
43132             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43133                 e.stopEvent();
43134             }
43135         };
43136         
43137         this.inputEl().on("keypress", keyPress, this);
43138         
43139     },
43140     
43141     onTriggerClick : function(e)
43142     {   
43143         if(this.disabled){
43144             return;
43145         }
43146         
43147         this.page = 0;
43148         this.loadNext = false;
43149         
43150         if(this.isExpanded()){
43151             this.collapse();
43152             return;
43153         }
43154         
43155         this.hasFocus = true;
43156         
43157         if(this.triggerAction == 'all') {
43158             this.doQuery(this.allQuery, true);
43159             return;
43160         }
43161         
43162         this.doQuery(this.getRawValue());
43163     },
43164     
43165     getCurrency : function()
43166     {   
43167         var v = this.currencyEl().getValue();
43168         
43169         return v;
43170     },
43171     
43172     restrictHeight : function()
43173     {
43174         this.list.alignTo(this.currencyEl(), this.listAlign);
43175         this.list.alignTo(this.currencyEl(), this.listAlign);
43176     },
43177     
43178     onViewClick : function(view, doFocus, el, e)
43179     {
43180         var index = this.view.getSelectedIndexes()[0];
43181         
43182         var r = this.store.getAt(index);
43183         
43184         if(r){
43185             this.onSelect(r, index);
43186         }
43187     },
43188     
43189     onSelect : function(record, index){
43190         
43191         if(this.fireEvent('beforeselect', this, record, index) !== false){
43192         
43193             this.setFromCurrencyData(index > -1 ? record.data : false);
43194             
43195             this.collapse();
43196             
43197             this.fireEvent('select', this, record, index);
43198         }
43199     },
43200     
43201     setFromCurrencyData : function(o)
43202     {
43203         var currency = '';
43204         
43205         this.lastCurrency = o;
43206         
43207         if (this.currencyField) {
43208             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43209         } else {
43210             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43211         }
43212         
43213         this.lastSelectionText = currency;
43214         
43215         //setting default currency
43216         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43217             this.setCurrency(this.defaultCurrency);
43218             return;
43219         }
43220         
43221         this.setCurrency(currency);
43222     },
43223     
43224     setFromData : function(o)
43225     {
43226         var c = {};
43227         
43228         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43229         
43230         this.setFromCurrencyData(c);
43231         
43232         var value = '';
43233         
43234         if (this.name) {
43235             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43236         } else {
43237             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43238         }
43239         
43240         this.setValue(value);
43241         
43242     },
43243     
43244     setCurrency : function(v)
43245     {   
43246         this.currencyValue = v;
43247         
43248         if(this.rendered){
43249             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43250             this.validate();
43251         }
43252     },
43253     
43254     setValue : function(v)
43255     {
43256         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43257         
43258         this.value = v;
43259         
43260         if(this.rendered){
43261             
43262             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43263             
43264             this.inputEl().dom.value = (v == '') ? '' :
43265                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43266             
43267             if(!this.allowZero && v === '0') {
43268                 this.hiddenEl().dom.value = '';
43269                 this.inputEl().dom.value = '';
43270             }
43271             
43272             this.validate();
43273         }
43274     },
43275     
43276     getRawValue : function()
43277     {
43278         var v = this.inputEl().getValue();
43279         
43280         return v;
43281     },
43282     
43283     getValue : function()
43284     {
43285         return this.fixPrecision(this.parseValue(this.getRawValue()));
43286     },
43287     
43288     parseValue : function(value)
43289     {
43290         if(this.thousandsDelimiter) {
43291             value += "";
43292             r = new RegExp(",", "g");
43293             value = value.replace(r, "");
43294         }
43295         
43296         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43297         return isNaN(value) ? '' : value;
43298         
43299     },
43300     
43301     fixPrecision : function(value)
43302     {
43303         if(this.thousandsDelimiter) {
43304             value += "";
43305             r = new RegExp(",", "g");
43306             value = value.replace(r, "");
43307         }
43308         
43309         var nan = isNaN(value);
43310         
43311         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43312             return nan ? '' : value;
43313         }
43314         return parseFloat(value).toFixed(this.decimalPrecision);
43315     },
43316     
43317     decimalPrecisionFcn : function(v)
43318     {
43319         return Math.floor(v);
43320     },
43321     
43322     validateValue : function(value)
43323     {
43324         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43325             return false;
43326         }
43327         
43328         var num = this.parseValue(value);
43329         
43330         if(isNaN(num)){
43331             this.markInvalid(String.format(this.nanText, value));
43332             return false;
43333         }
43334         
43335         if(num < this.minValue){
43336             this.markInvalid(String.format(this.minText, this.minValue));
43337             return false;
43338         }
43339         
43340         if(num > this.maxValue){
43341             this.markInvalid(String.format(this.maxText, this.maxValue));
43342             return false;
43343         }
43344         
43345         return true;
43346     },
43347     
43348     validate : function()
43349     {
43350         if(this.disabled || this.allowBlank){
43351             this.markValid();
43352             return true;
43353         }
43354         
43355         var currency = this.getCurrency();
43356         
43357         if(this.validateValue(this.getRawValue()) && currency.length){
43358             this.markValid();
43359             return true;
43360         }
43361         
43362         this.markInvalid();
43363         return false;
43364     },
43365     
43366     getName: function()
43367     {
43368         return this.name;
43369     },
43370     
43371     beforeBlur : function()
43372     {
43373         if(!this.castInt){
43374             return;
43375         }
43376         
43377         var v = this.parseValue(this.getRawValue());
43378         
43379         if(v || v == 0){
43380             this.setValue(v);
43381         }
43382     },
43383     
43384     onBlur : function()
43385     {
43386         this.beforeBlur();
43387         
43388         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43389             //this.el.removeClass(this.focusClass);
43390         }
43391         
43392         this.hasFocus = false;
43393         
43394         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43395             this.validate();
43396         }
43397         
43398         var v = this.getValue();
43399         
43400         if(String(v) !== String(this.startValue)){
43401             this.fireEvent('change', this, v, this.startValue);
43402         }
43403         
43404         this.fireEvent("blur", this);
43405     },
43406     
43407     inputEl : function()
43408     {
43409         return this.el.select('.roo-money-amount-input', true).first();
43410     },
43411     
43412     currencyEl : function()
43413     {
43414         return this.el.select('.roo-money-currency-input', true).first();
43415     },
43416     
43417     hiddenEl : function()
43418     {
43419         return this.el.select('input.hidden-number-input',true).first();
43420     }
43421     
43422 });/**
43423  * @class Roo.bootstrap.BezierSignature
43424  * @extends Roo.bootstrap.Component
43425  * Bootstrap BezierSignature class
43426  * This script refer to:
43427  *    Title: Signature Pad
43428  *    Author: szimek
43429  *    Availability: https://github.com/szimek/signature_pad
43430  *
43431  * @constructor
43432  * Create a new BezierSignature
43433  * @param {Object} config The config object
43434  */
43435
43436 Roo.bootstrap.BezierSignature = function(config){
43437     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43438     this.addEvents({
43439         "resize" : true
43440     });
43441 };
43442
43443 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43444 {
43445      
43446     curve_data: [],
43447     
43448     is_empty: true,
43449     
43450     mouse_btn_down: true,
43451     
43452     /**
43453      * @cfg {int} canvas height
43454      */
43455     canvas_height: '200px',
43456     
43457     /**
43458      * @cfg {float|function} Radius of a single dot.
43459      */ 
43460     dot_size: false,
43461     
43462     /**
43463      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43464      */
43465     min_width: 0.5,
43466     
43467     /**
43468      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43469      */
43470     max_width: 2.5,
43471     
43472     /**
43473      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43474      */
43475     throttle: 16,
43476     
43477     /**
43478      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43479      */
43480     min_distance: 5,
43481     
43482     /**
43483      * @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.
43484      */
43485     bg_color: 'rgba(0, 0, 0, 0)',
43486     
43487     /**
43488      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43489      */
43490     dot_color: 'black',
43491     
43492     /**
43493      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43494      */ 
43495     velocity_filter_weight: 0.7,
43496     
43497     /**
43498      * @cfg {function} Callback when stroke begin. 
43499      */
43500     onBegin: false,
43501     
43502     /**
43503      * @cfg {function} Callback when stroke end.
43504      */
43505     onEnd: false,
43506     
43507     getAutoCreate : function()
43508     {
43509         var cls = 'roo-signature column';
43510         
43511         if(this.cls){
43512             cls += ' ' + this.cls;
43513         }
43514         
43515         var col_sizes = [
43516             'lg',
43517             'md',
43518             'sm',
43519             'xs'
43520         ];
43521         
43522         for(var i = 0; i < col_sizes.length; i++) {
43523             if(this[col_sizes[i]]) {
43524                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43525             }
43526         }
43527         
43528         var cfg = {
43529             tag: 'div',
43530             cls: cls,
43531             cn: [
43532                 {
43533                     tag: 'div',
43534                     cls: 'roo-signature-body',
43535                     cn: [
43536                         {
43537                             tag: 'canvas',
43538                             cls: 'roo-signature-body-canvas',
43539                             height: this.canvas_height,
43540                             width: this.canvas_width
43541                         }
43542                     ]
43543                 },
43544                 {
43545                     tag: 'input',
43546                     type: 'file',
43547                     style: 'display: none'
43548                 }
43549             ]
43550         };
43551         
43552         return cfg;
43553     },
43554     
43555     initEvents: function() 
43556     {
43557         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43558         
43559         var canvas = this.canvasEl();
43560         
43561         // mouse && touch event swapping...
43562         canvas.dom.style.touchAction = 'none';
43563         canvas.dom.style.msTouchAction = 'none';
43564         
43565         this.mouse_btn_down = false;
43566         canvas.on('mousedown', this._handleMouseDown, this);
43567         canvas.on('mousemove', this._handleMouseMove, this);
43568         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43569         
43570         if (window.PointerEvent) {
43571             canvas.on('pointerdown', this._handleMouseDown, this);
43572             canvas.on('pointermove', this._handleMouseMove, this);
43573             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43574         }
43575         
43576         if ('ontouchstart' in window) {
43577             canvas.on('touchstart', this._handleTouchStart, this);
43578             canvas.on('touchmove', this._handleTouchMove, this);
43579             canvas.on('touchend', this._handleTouchEnd, this);
43580         }
43581         
43582         Roo.EventManager.onWindowResize(this.resize, this, true);
43583         
43584         // file input event
43585         this.fileEl().on('change', this.uploadImage, this);
43586         
43587         this.clear();
43588         
43589         this.resize();
43590     },
43591     
43592     resize: function(){
43593         
43594         var canvas = this.canvasEl().dom;
43595         var ctx = this.canvasElCtx();
43596         var img_data = false;
43597         
43598         if(canvas.width > 0) {
43599             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43600         }
43601         // setting canvas width will clean img data
43602         canvas.width = 0;
43603         
43604         var style = window.getComputedStyle ? 
43605             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43606             
43607         var padding_left = parseInt(style.paddingLeft) || 0;
43608         var padding_right = parseInt(style.paddingRight) || 0;
43609         
43610         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43611         
43612         if(img_data) {
43613             ctx.putImageData(img_data, 0, 0);
43614         }
43615     },
43616     
43617     _handleMouseDown: function(e)
43618     {
43619         if (e.browserEvent.which === 1) {
43620             this.mouse_btn_down = true;
43621             this.strokeBegin(e);
43622         }
43623     },
43624     
43625     _handleMouseMove: function (e)
43626     {
43627         if (this.mouse_btn_down) {
43628             this.strokeMoveUpdate(e);
43629         }
43630     },
43631     
43632     _handleMouseUp: function (e)
43633     {
43634         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43635             this.mouse_btn_down = false;
43636             this.strokeEnd(e);
43637         }
43638     },
43639     
43640     _handleTouchStart: function (e) {
43641         
43642         e.preventDefault();
43643         if (e.browserEvent.targetTouches.length === 1) {
43644             // var touch = e.browserEvent.changedTouches[0];
43645             // this.strokeBegin(touch);
43646             
43647              this.strokeBegin(e); // assume e catching the correct xy...
43648         }
43649     },
43650     
43651     _handleTouchMove: function (e) {
43652         e.preventDefault();
43653         // var touch = event.targetTouches[0];
43654         // _this._strokeMoveUpdate(touch);
43655         this.strokeMoveUpdate(e);
43656     },
43657     
43658     _handleTouchEnd: function (e) {
43659         var wasCanvasTouched = e.target === this.canvasEl().dom;
43660         if (wasCanvasTouched) {
43661             e.preventDefault();
43662             // var touch = event.changedTouches[0];
43663             // _this._strokeEnd(touch);
43664             this.strokeEnd(e);
43665         }
43666     },
43667     
43668     reset: function () {
43669         this._lastPoints = [];
43670         this._lastVelocity = 0;
43671         this._lastWidth = (this.min_width + this.max_width) / 2;
43672         this.canvasElCtx().fillStyle = this.dot_color;
43673     },
43674     
43675     strokeMoveUpdate: function(e)
43676     {
43677         this.strokeUpdate(e);
43678         
43679         if (this.throttle) {
43680             this.throttleStroke(this.strokeUpdate, this.throttle);
43681         }
43682         else {
43683             this.strokeUpdate(e);
43684         }
43685     },
43686     
43687     strokeBegin: function(e)
43688     {
43689         var newPointGroup = {
43690             color: this.dot_color,
43691             points: []
43692         };
43693         
43694         if (typeof this.onBegin === 'function') {
43695             this.onBegin(e);
43696         }
43697         
43698         this.curve_data.push(newPointGroup);
43699         this.reset();
43700         this.strokeUpdate(e);
43701     },
43702     
43703     strokeUpdate: function(e)
43704     {
43705         var rect = this.canvasEl().dom.getBoundingClientRect();
43706         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43707         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43708         var lastPoints = lastPointGroup.points;
43709         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43710         var isLastPointTooClose = lastPoint
43711             ? point.distanceTo(lastPoint) <= this.min_distance
43712             : false;
43713         var color = lastPointGroup.color;
43714         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43715             var curve = this.addPoint(point);
43716             if (!lastPoint) {
43717                 this.drawDot({color: color, point: point});
43718             }
43719             else if (curve) {
43720                 this.drawCurve({color: color, curve: curve});
43721             }
43722             lastPoints.push({
43723                 time: point.time,
43724                 x: point.x,
43725                 y: point.y
43726             });
43727         }
43728     },
43729     
43730     strokeEnd: function(e)
43731     {
43732         this.strokeUpdate(e);
43733         if (typeof this.onEnd === 'function') {
43734             this.onEnd(e);
43735         }
43736     },
43737     
43738     addPoint:  function (point) {
43739         var _lastPoints = this._lastPoints;
43740         _lastPoints.push(point);
43741         if (_lastPoints.length > 2) {
43742             if (_lastPoints.length === 3) {
43743                 _lastPoints.unshift(_lastPoints[0]);
43744             }
43745             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43746             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43747             _lastPoints.shift();
43748             return curve;
43749         }
43750         return null;
43751     },
43752     
43753     calculateCurveWidths: function (startPoint, endPoint) {
43754         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43755             (1 - this.velocity_filter_weight) * this._lastVelocity;
43756
43757         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43758         var widths = {
43759             end: newWidth,
43760             start: this._lastWidth
43761         };
43762         
43763         this._lastVelocity = velocity;
43764         this._lastWidth = newWidth;
43765         return widths;
43766     },
43767     
43768     drawDot: function (_a) {
43769         var color = _a.color, point = _a.point;
43770         var ctx = this.canvasElCtx();
43771         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43772         ctx.beginPath();
43773         this.drawCurveSegment(point.x, point.y, width);
43774         ctx.closePath();
43775         ctx.fillStyle = color;
43776         ctx.fill();
43777     },
43778     
43779     drawCurve: function (_a) {
43780         var color = _a.color, curve = _a.curve;
43781         var ctx = this.canvasElCtx();
43782         var widthDelta = curve.endWidth - curve.startWidth;
43783         var drawSteps = Math.floor(curve.length()) * 2;
43784         ctx.beginPath();
43785         ctx.fillStyle = color;
43786         for (var i = 0; i < drawSteps; i += 1) {
43787         var t = i / drawSteps;
43788         var tt = t * t;
43789         var ttt = tt * t;
43790         var u = 1 - t;
43791         var uu = u * u;
43792         var uuu = uu * u;
43793         var x = uuu * curve.startPoint.x;
43794         x += 3 * uu * t * curve.control1.x;
43795         x += 3 * u * tt * curve.control2.x;
43796         x += ttt * curve.endPoint.x;
43797         var y = uuu * curve.startPoint.y;
43798         y += 3 * uu * t * curve.control1.y;
43799         y += 3 * u * tt * curve.control2.y;
43800         y += ttt * curve.endPoint.y;
43801         var width = curve.startWidth + ttt * widthDelta;
43802         this.drawCurveSegment(x, y, width);
43803         }
43804         ctx.closePath();
43805         ctx.fill();
43806     },
43807     
43808     drawCurveSegment: function (x, y, width) {
43809         var ctx = this.canvasElCtx();
43810         ctx.moveTo(x, y);
43811         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43812         this.is_empty = false;
43813     },
43814     
43815     clear: function()
43816     {
43817         var ctx = this.canvasElCtx();
43818         var canvas = this.canvasEl().dom;
43819         ctx.fillStyle = this.bg_color;
43820         ctx.clearRect(0, 0, canvas.width, canvas.height);
43821         ctx.fillRect(0, 0, canvas.width, canvas.height);
43822         this.curve_data = [];
43823         this.reset();
43824         this.is_empty = true;
43825     },
43826     
43827     fileEl: function()
43828     {
43829         return  this.el.select('input',true).first();
43830     },
43831     
43832     canvasEl: function()
43833     {
43834         return this.el.select('canvas',true).first();
43835     },
43836     
43837     canvasElCtx: function()
43838     {
43839         return this.el.select('canvas',true).first().dom.getContext('2d');
43840     },
43841     
43842     getImage: function(type)
43843     {
43844         if(this.is_empty) {
43845             return false;
43846         }
43847         
43848         // encryption ?
43849         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43850     },
43851     
43852     drawFromImage: function(img_src)
43853     {
43854         var img = new Image();
43855         
43856         img.onload = function(){
43857             this.canvasElCtx().drawImage(img, 0, 0);
43858         }.bind(this);
43859         
43860         img.src = img_src;
43861         
43862         this.is_empty = false;
43863     },
43864     
43865     selectImage: function()
43866     {
43867         this.fileEl().dom.click();
43868     },
43869     
43870     uploadImage: function(e)
43871     {
43872         var reader = new FileReader();
43873         
43874         reader.onload = function(e){
43875             var img = new Image();
43876             img.onload = function(){
43877                 this.reset();
43878                 this.canvasElCtx().drawImage(img, 0, 0);
43879             }.bind(this);
43880             img.src = e.target.result;
43881         }.bind(this);
43882         
43883         reader.readAsDataURL(e.target.files[0]);
43884     },
43885     
43886     // Bezier Point Constructor
43887     Point: (function () {
43888         function Point(x, y, time) {
43889             this.x = x;
43890             this.y = y;
43891             this.time = time || Date.now();
43892         }
43893         Point.prototype.distanceTo = function (start) {
43894             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43895         };
43896         Point.prototype.equals = function (other) {
43897             return this.x === other.x && this.y === other.y && this.time === other.time;
43898         };
43899         Point.prototype.velocityFrom = function (start) {
43900             return this.time !== start.time
43901             ? this.distanceTo(start) / (this.time - start.time)
43902             : 0;
43903         };
43904         return Point;
43905     }()),
43906     
43907     
43908     // Bezier Constructor
43909     Bezier: (function () {
43910         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43911             this.startPoint = startPoint;
43912             this.control2 = control2;
43913             this.control1 = control1;
43914             this.endPoint = endPoint;
43915             this.startWidth = startWidth;
43916             this.endWidth = endWidth;
43917         }
43918         Bezier.fromPoints = function (points, widths, scope) {
43919             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43920             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43921             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43922         };
43923         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43924             var dx1 = s1.x - s2.x;
43925             var dy1 = s1.y - s2.y;
43926             var dx2 = s2.x - s3.x;
43927             var dy2 = s2.y - s3.y;
43928             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43929             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43930             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43931             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43932             var dxm = m1.x - m2.x;
43933             var dym = m1.y - m2.y;
43934             var k = l2 / (l1 + l2);
43935             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43936             var tx = s2.x - cm.x;
43937             var ty = s2.y - cm.y;
43938             return {
43939                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43940                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43941             };
43942         };
43943         Bezier.prototype.length = function () {
43944             var steps = 10;
43945             var length = 0;
43946             var px;
43947             var py;
43948             for (var i = 0; i <= steps; i += 1) {
43949                 var t = i / steps;
43950                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43951                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43952                 if (i > 0) {
43953                     var xdiff = cx - px;
43954                     var ydiff = cy - py;
43955                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43956                 }
43957                 px = cx;
43958                 py = cy;
43959             }
43960             return length;
43961         };
43962         Bezier.prototype.point = function (t, start, c1, c2, end) {
43963             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43964             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43965             + (3.0 * c2 * (1.0 - t) * t * t)
43966             + (end * t * t * t);
43967         };
43968         return Bezier;
43969     }()),
43970     
43971     throttleStroke: function(fn, wait) {
43972       if (wait === void 0) { wait = 250; }
43973       var previous = 0;
43974       var timeout = null;
43975       var result;
43976       var storedContext;
43977       var storedArgs;
43978       var later = function () {
43979           previous = Date.now();
43980           timeout = null;
43981           result = fn.apply(storedContext, storedArgs);
43982           if (!timeout) {
43983               storedContext = null;
43984               storedArgs = [];
43985           }
43986       };
43987       return function wrapper() {
43988           var args = [];
43989           for (var _i = 0; _i < arguments.length; _i++) {
43990               args[_i] = arguments[_i];
43991           }
43992           var now = Date.now();
43993           var remaining = wait - (now - previous);
43994           storedContext = this;
43995           storedArgs = args;
43996           if (remaining <= 0 || remaining > wait) {
43997               if (timeout) {
43998                   clearTimeout(timeout);
43999                   timeout = null;
44000               }
44001               previous = now;
44002               result = fn.apply(storedContext, storedArgs);
44003               if (!timeout) {
44004                   storedContext = null;
44005                   storedArgs = [];
44006               }
44007           }
44008           else if (!timeout) {
44009               timeout = window.setTimeout(later, remaining);
44010           }
44011           return result;
44012       };
44013   }
44014   
44015 });
44016
44017  
44018
44019