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          
26146         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26147         this.markdownEl.on('click', this.toggleTextEdit, this);
26148         this.on('blur', this.toggleTextEdit, this);
26149         this.on('specialkey', this.resizeTextArea, this);
26150     },
26151     
26152     toggleTextEdit : function()
26153     {
26154         var sh = this.markdownEl.getHeight();
26155         this.inputEl().addClass('d-none');
26156         this.markdownEl.addClass('d-none');
26157         if (!this.editing) {
26158             // show editor?
26159             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26160             this.inputEl().removeClass('d-none');
26161             this.inputEl().focus();
26162             this.editing = true;
26163             return;
26164         }
26165         // show showdown...
26166         this.updateMarkdown();
26167         this.markdownEl.removeClass('d-none');
26168         this.editing = false;
26169         return;
26170     },
26171     updateMarkdown : function()
26172     {
26173         if (this.getValue() == '') {
26174             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26175             return;
26176         }
26177  
26178         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26179     },
26180     
26181     resizeTextArea: function () {
26182         
26183         var sh = 100;
26184         Roo.log([sh, this.getValue().split("\n").length * 30]);
26185         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26186     },
26187     setValue : function(val)
26188     {
26189         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26190         if (!this.editing) {
26191             this.updateMarkdown();
26192         }
26193         
26194     },
26195     focus : function()
26196     {
26197         if (!this.editing) {
26198             this.toggleTextEdit();
26199         }
26200         
26201     }
26202
26203
26204 });
26205 /**
26206  * @class Roo.bootstrap.Table.AbstractSelectionModel
26207  * @extends Roo.util.Observable
26208  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26209  * implemented by descendant classes.  This class should not be directly instantiated.
26210  * @constructor
26211  */
26212 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26213     this.locked = false;
26214     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26215 };
26216
26217
26218 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26219     /** @ignore Called by the grid automatically. Do not call directly. */
26220     init : function(grid){
26221         this.grid = grid;
26222         this.initEvents();
26223     },
26224
26225     /**
26226      * Locks the selections.
26227      */
26228     lock : function(){
26229         this.locked = true;
26230     },
26231
26232     /**
26233      * Unlocks the selections.
26234      */
26235     unlock : function(){
26236         this.locked = false;
26237     },
26238
26239     /**
26240      * Returns true if the selections are locked.
26241      * @return {Boolean}
26242      */
26243     isLocked : function(){
26244         return this.locked;
26245     },
26246     
26247     
26248     initEvents : function ()
26249     {
26250         
26251     }
26252 });
26253 /**
26254  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26255  * @class Roo.bootstrap.Table.RowSelectionModel
26256  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26257  * It supports multiple selections and keyboard selection/navigation. 
26258  * @constructor
26259  * @param {Object} config
26260  */
26261
26262 Roo.bootstrap.Table.RowSelectionModel = function(config){
26263     Roo.apply(this, config);
26264     this.selections = new Roo.util.MixedCollection(false, function(o){
26265         return o.id;
26266     });
26267
26268     this.last = false;
26269     this.lastActive = false;
26270
26271     this.addEvents({
26272         /**
26273              * @event selectionchange
26274              * Fires when the selection changes
26275              * @param {SelectionModel} this
26276              */
26277             "selectionchange" : true,
26278         /**
26279              * @event afterselectionchange
26280              * Fires after the selection changes (eg. by key press or clicking)
26281              * @param {SelectionModel} this
26282              */
26283             "afterselectionchange" : true,
26284         /**
26285              * @event beforerowselect
26286              * Fires when a row is selected being selected, return false to cancel.
26287              * @param {SelectionModel} this
26288              * @param {Number} rowIndex The selected index
26289              * @param {Boolean} keepExisting False if other selections will be cleared
26290              */
26291             "beforerowselect" : true,
26292         /**
26293              * @event rowselect
26294              * Fires when a row is selected.
26295              * @param {SelectionModel} this
26296              * @param {Number} rowIndex The selected index
26297              * @param {Roo.data.Record} r The record
26298              */
26299             "rowselect" : true,
26300         /**
26301              * @event rowdeselect
26302              * Fires when a row is deselected.
26303              * @param {SelectionModel} this
26304              * @param {Number} rowIndex The selected index
26305              */
26306         "rowdeselect" : true
26307     });
26308     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26309     this.locked = false;
26310  };
26311
26312 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26313     /**
26314      * @cfg {Boolean} singleSelect
26315      * True to allow selection of only one row at a time (defaults to false)
26316      */
26317     singleSelect : false,
26318
26319     // private
26320     initEvents : function()
26321     {
26322
26323         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26324         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26325         //}else{ // allow click to work like normal
26326          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26327         //}
26328         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26329         this.grid.on("rowclick", this.handleMouseDown, this);
26330         
26331         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26332             "up" : function(e){
26333                 if(!e.shiftKey){
26334                     this.selectPrevious(e.shiftKey);
26335                 }else if(this.last !== false && this.lastActive !== false){
26336                     var last = this.last;
26337                     this.selectRange(this.last,  this.lastActive-1);
26338                     this.grid.getView().focusRow(this.lastActive);
26339                     if(last !== false){
26340                         this.last = last;
26341                     }
26342                 }else{
26343                     this.selectFirstRow();
26344                 }
26345                 this.fireEvent("afterselectionchange", this);
26346             },
26347             "down" : function(e){
26348                 if(!e.shiftKey){
26349                     this.selectNext(e.shiftKey);
26350                 }else if(this.last !== false && this.lastActive !== false){
26351                     var last = this.last;
26352                     this.selectRange(this.last,  this.lastActive+1);
26353                     this.grid.getView().focusRow(this.lastActive);
26354                     if(last !== false){
26355                         this.last = last;
26356                     }
26357                 }else{
26358                     this.selectFirstRow();
26359                 }
26360                 this.fireEvent("afterselectionchange", this);
26361             },
26362             scope: this
26363         });
26364         this.grid.store.on('load', function(){
26365             this.selections.clear();
26366         },this);
26367         /*
26368         var view = this.grid.view;
26369         view.on("refresh", this.onRefresh, this);
26370         view.on("rowupdated", this.onRowUpdated, this);
26371         view.on("rowremoved", this.onRemove, this);
26372         */
26373     },
26374
26375     // private
26376     onRefresh : function()
26377     {
26378         var ds = this.grid.store, i, v = this.grid.view;
26379         var s = this.selections;
26380         s.each(function(r){
26381             if((i = ds.indexOfId(r.id)) != -1){
26382                 v.onRowSelect(i);
26383             }else{
26384                 s.remove(r);
26385             }
26386         });
26387     },
26388
26389     // private
26390     onRemove : function(v, index, r){
26391         this.selections.remove(r);
26392     },
26393
26394     // private
26395     onRowUpdated : function(v, index, r){
26396         if(this.isSelected(r)){
26397             v.onRowSelect(index);
26398         }
26399     },
26400
26401     /**
26402      * Select records.
26403      * @param {Array} records The records to select
26404      * @param {Boolean} keepExisting (optional) True to keep existing selections
26405      */
26406     selectRecords : function(records, keepExisting)
26407     {
26408         if(!keepExisting){
26409             this.clearSelections();
26410         }
26411             var ds = this.grid.store;
26412         for(var i = 0, len = records.length; i < len; i++){
26413             this.selectRow(ds.indexOf(records[i]), true);
26414         }
26415     },
26416
26417     /**
26418      * Gets the number of selected rows.
26419      * @return {Number}
26420      */
26421     getCount : function(){
26422         return this.selections.length;
26423     },
26424
26425     /**
26426      * Selects the first row in the grid.
26427      */
26428     selectFirstRow : function(){
26429         this.selectRow(0);
26430     },
26431
26432     /**
26433      * Select the last row.
26434      * @param {Boolean} keepExisting (optional) True to keep existing selections
26435      */
26436     selectLastRow : function(keepExisting){
26437         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26438         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26439     },
26440
26441     /**
26442      * Selects the row immediately following the last selected row.
26443      * @param {Boolean} keepExisting (optional) True to keep existing selections
26444      */
26445     selectNext : function(keepExisting)
26446     {
26447             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26448             this.selectRow(this.last+1, keepExisting);
26449             this.grid.getView().focusRow(this.last);
26450         }
26451     },
26452
26453     /**
26454      * Selects the row that precedes the last selected row.
26455      * @param {Boolean} keepExisting (optional) True to keep existing selections
26456      */
26457     selectPrevious : function(keepExisting){
26458         if(this.last){
26459             this.selectRow(this.last-1, keepExisting);
26460             this.grid.getView().focusRow(this.last);
26461         }
26462     },
26463
26464     /**
26465      * Returns the selected records
26466      * @return {Array} Array of selected records
26467      */
26468     getSelections : function(){
26469         return [].concat(this.selections.items);
26470     },
26471
26472     /**
26473      * Returns the first selected record.
26474      * @return {Record}
26475      */
26476     getSelected : function(){
26477         return this.selections.itemAt(0);
26478     },
26479
26480
26481     /**
26482      * Clears all selections.
26483      */
26484     clearSelections : function(fast)
26485     {
26486         if(this.locked) {
26487             return;
26488         }
26489         if(fast !== true){
26490                 var ds = this.grid.store;
26491             var s = this.selections;
26492             s.each(function(r){
26493                 this.deselectRow(ds.indexOfId(r.id));
26494             }, this);
26495             s.clear();
26496         }else{
26497             this.selections.clear();
26498         }
26499         this.last = false;
26500     },
26501
26502
26503     /**
26504      * Selects all rows.
26505      */
26506     selectAll : function(){
26507         if(this.locked) {
26508             return;
26509         }
26510         this.selections.clear();
26511         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26512             this.selectRow(i, true);
26513         }
26514     },
26515
26516     /**
26517      * Returns True if there is a selection.
26518      * @return {Boolean}
26519      */
26520     hasSelection : function(){
26521         return this.selections.length > 0;
26522     },
26523
26524     /**
26525      * Returns True if the specified row is selected.
26526      * @param {Number/Record} record The record or index of the record to check
26527      * @return {Boolean}
26528      */
26529     isSelected : function(index){
26530             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26531         return (r && this.selections.key(r.id) ? true : false);
26532     },
26533
26534     /**
26535      * Returns True if the specified record id is selected.
26536      * @param {String} id The id of record to check
26537      * @return {Boolean}
26538      */
26539     isIdSelected : function(id){
26540         return (this.selections.key(id) ? true : false);
26541     },
26542
26543
26544     // private
26545     handleMouseDBClick : function(e, t){
26546         
26547     },
26548     // private
26549     handleMouseDown : function(e, t)
26550     {
26551             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26552         if(this.isLocked() || rowIndex < 0 ){
26553             return;
26554         };
26555         if(e.shiftKey && this.last !== false){
26556             var last = this.last;
26557             this.selectRange(last, rowIndex, e.ctrlKey);
26558             this.last = last; // reset the last
26559             t.focus();
26560     
26561         }else{
26562             var isSelected = this.isSelected(rowIndex);
26563             //Roo.log("select row:" + rowIndex);
26564             if(isSelected){
26565                 this.deselectRow(rowIndex);
26566             } else {
26567                         this.selectRow(rowIndex, true);
26568             }
26569     
26570             /*
26571                 if(e.button !== 0 && isSelected){
26572                 alert('rowIndex 2: ' + rowIndex);
26573                     view.focusRow(rowIndex);
26574                 }else if(e.ctrlKey && isSelected){
26575                     this.deselectRow(rowIndex);
26576                 }else if(!isSelected){
26577                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26578                     view.focusRow(rowIndex);
26579                 }
26580             */
26581         }
26582         this.fireEvent("afterselectionchange", this);
26583     },
26584     // private
26585     handleDragableRowClick :  function(grid, rowIndex, e) 
26586     {
26587         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26588             this.selectRow(rowIndex, false);
26589             grid.view.focusRow(rowIndex);
26590              this.fireEvent("afterselectionchange", this);
26591         }
26592     },
26593     
26594     /**
26595      * Selects multiple rows.
26596      * @param {Array} rows Array of the indexes of the row to select
26597      * @param {Boolean} keepExisting (optional) True to keep existing selections
26598      */
26599     selectRows : function(rows, keepExisting){
26600         if(!keepExisting){
26601             this.clearSelections();
26602         }
26603         for(var i = 0, len = rows.length; i < len; i++){
26604             this.selectRow(rows[i], true);
26605         }
26606     },
26607
26608     /**
26609      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26610      * @param {Number} startRow The index of the first row in the range
26611      * @param {Number} endRow The index of the last row in the range
26612      * @param {Boolean} keepExisting (optional) True to retain existing selections
26613      */
26614     selectRange : function(startRow, endRow, keepExisting){
26615         if(this.locked) {
26616             return;
26617         }
26618         if(!keepExisting){
26619             this.clearSelections();
26620         }
26621         if(startRow <= endRow){
26622             for(var i = startRow; i <= endRow; i++){
26623                 this.selectRow(i, true);
26624             }
26625         }else{
26626             for(var i = startRow; i >= endRow; i--){
26627                 this.selectRow(i, true);
26628             }
26629         }
26630     },
26631
26632     /**
26633      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26634      * @param {Number} startRow The index of the first row in the range
26635      * @param {Number} endRow The index of the last row in the range
26636      */
26637     deselectRange : function(startRow, endRow, preventViewNotify){
26638         if(this.locked) {
26639             return;
26640         }
26641         for(var i = startRow; i <= endRow; i++){
26642             this.deselectRow(i, preventViewNotify);
26643         }
26644     },
26645
26646     /**
26647      * Selects a row.
26648      * @param {Number} row The index of the row to select
26649      * @param {Boolean} keepExisting (optional) True to keep existing selections
26650      */
26651     selectRow : function(index, keepExisting, preventViewNotify)
26652     {
26653             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26654             return;
26655         }
26656         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26657             if(!keepExisting || this.singleSelect){
26658                 this.clearSelections();
26659             }
26660             
26661             var r = this.grid.store.getAt(index);
26662             //console.log('selectRow - record id :' + r.id);
26663             
26664             this.selections.add(r);
26665             this.last = this.lastActive = index;
26666             if(!preventViewNotify){
26667                 var proxy = new Roo.Element(
26668                                 this.grid.getRowDom(index)
26669                 );
26670                 proxy.addClass('bg-info info');
26671             }
26672             this.fireEvent("rowselect", this, index, r);
26673             this.fireEvent("selectionchange", this);
26674         }
26675     },
26676
26677     /**
26678      * Deselects a row.
26679      * @param {Number} row The index of the row to deselect
26680      */
26681     deselectRow : function(index, preventViewNotify)
26682     {
26683         if(this.locked) {
26684             return;
26685         }
26686         if(this.last == index){
26687             this.last = false;
26688         }
26689         if(this.lastActive == index){
26690             this.lastActive = false;
26691         }
26692         
26693         var r = this.grid.store.getAt(index);
26694         if (!r) {
26695             return;
26696         }
26697         
26698         this.selections.remove(r);
26699         //.console.log('deselectRow - record id :' + r.id);
26700         if(!preventViewNotify){
26701         
26702             var proxy = new Roo.Element(
26703                 this.grid.getRowDom(index)
26704             );
26705             proxy.removeClass('bg-info info');
26706         }
26707         this.fireEvent("rowdeselect", this, index);
26708         this.fireEvent("selectionchange", this);
26709     },
26710
26711     // private
26712     restoreLast : function(){
26713         if(this._last){
26714             this.last = this._last;
26715         }
26716     },
26717
26718     // private
26719     acceptsNav : function(row, col, cm){
26720         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26721     },
26722
26723     // private
26724     onEditorKey : function(field, e){
26725         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26726         if(k == e.TAB){
26727             e.stopEvent();
26728             ed.completeEdit();
26729             if(e.shiftKey){
26730                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26731             }else{
26732                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26733             }
26734         }else if(k == e.ENTER && !e.ctrlKey){
26735             e.stopEvent();
26736             ed.completeEdit();
26737             if(e.shiftKey){
26738                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26739             }else{
26740                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26741             }
26742         }else if(k == e.ESC){
26743             ed.cancelEdit();
26744         }
26745         if(newCell){
26746             g.startEditing(newCell[0], newCell[1]);
26747         }
26748     }
26749 });
26750 /*
26751  * Based on:
26752  * Ext JS Library 1.1.1
26753  * Copyright(c) 2006-2007, Ext JS, LLC.
26754  *
26755  * Originally Released Under LGPL - original licence link has changed is not relivant.
26756  *
26757  * Fork - LGPL
26758  * <script type="text/javascript">
26759  */
26760  
26761 /**
26762  * @class Roo.bootstrap.PagingToolbar
26763  * @extends Roo.bootstrap.NavSimplebar
26764  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26765  * @constructor
26766  * Create a new PagingToolbar
26767  * @param {Object} config The config object
26768  * @param {Roo.data.Store} store
26769  */
26770 Roo.bootstrap.PagingToolbar = function(config)
26771 {
26772     // old args format still supported... - xtype is prefered..
26773         // created from xtype...
26774     
26775     this.ds = config.dataSource;
26776     
26777     if (config.store && !this.ds) {
26778         this.store= Roo.factory(config.store, Roo.data);
26779         this.ds = this.store;
26780         this.ds.xmodule = this.xmodule || false;
26781     }
26782     
26783     this.toolbarItems = [];
26784     if (config.items) {
26785         this.toolbarItems = config.items;
26786     }
26787     
26788     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26789     
26790     this.cursor = 0;
26791     
26792     if (this.ds) { 
26793         this.bind(this.ds);
26794     }
26795     
26796     if (Roo.bootstrap.version == 4) {
26797         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26798     } else {
26799         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26800     }
26801     
26802 };
26803
26804 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26805     /**
26806      * @cfg {Roo.data.Store} dataSource
26807      * The underlying data store providing the paged data
26808      */
26809     /**
26810      * @cfg {String/HTMLElement/Element} container
26811      * container The id or element that will contain the toolbar
26812      */
26813     /**
26814      * @cfg {Boolean} displayInfo
26815      * True to display the displayMsg (defaults to false)
26816      */
26817     /**
26818      * @cfg {Number} pageSize
26819      * The number of records to display per page (defaults to 20)
26820      */
26821     pageSize: 20,
26822     /**
26823      * @cfg {String} displayMsg
26824      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26825      */
26826     displayMsg : 'Displaying {0} - {1} of {2}',
26827     /**
26828      * @cfg {String} emptyMsg
26829      * The message to display when no records are found (defaults to "No data to display")
26830      */
26831     emptyMsg : 'No data to display',
26832     /**
26833      * Customizable piece of the default paging text (defaults to "Page")
26834      * @type String
26835      */
26836     beforePageText : "Page",
26837     /**
26838      * Customizable piece of the default paging text (defaults to "of %0")
26839      * @type String
26840      */
26841     afterPageText : "of {0}",
26842     /**
26843      * Customizable piece of the default paging text (defaults to "First Page")
26844      * @type String
26845      */
26846     firstText : "First Page",
26847     /**
26848      * Customizable piece of the default paging text (defaults to "Previous Page")
26849      * @type String
26850      */
26851     prevText : "Previous Page",
26852     /**
26853      * Customizable piece of the default paging text (defaults to "Next Page")
26854      * @type String
26855      */
26856     nextText : "Next Page",
26857     /**
26858      * Customizable piece of the default paging text (defaults to "Last Page")
26859      * @type String
26860      */
26861     lastText : "Last Page",
26862     /**
26863      * Customizable piece of the default paging text (defaults to "Refresh")
26864      * @type String
26865      */
26866     refreshText : "Refresh",
26867
26868     buttons : false,
26869     // private
26870     onRender : function(ct, position) 
26871     {
26872         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26873         this.navgroup.parentId = this.id;
26874         this.navgroup.onRender(this.el, null);
26875         // add the buttons to the navgroup
26876         
26877         if(this.displayInfo){
26878             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26879             this.displayEl = this.el.select('.x-paging-info', true).first();
26880 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26881 //            this.displayEl = navel.el.select('span',true).first();
26882         }
26883         
26884         var _this = this;
26885         
26886         if(this.buttons){
26887             Roo.each(_this.buttons, function(e){ // this might need to use render????
26888                Roo.factory(e).render(_this.el);
26889             });
26890         }
26891             
26892         Roo.each(_this.toolbarItems, function(e) {
26893             _this.navgroup.addItem(e);
26894         });
26895         
26896         
26897         this.first = this.navgroup.addItem({
26898             tooltip: this.firstText,
26899             cls: "prev btn-outline-secondary",
26900             html : ' <i class="fa fa-step-backward"></i>',
26901             disabled: true,
26902             preventDefault: true,
26903             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26904         });
26905         
26906         this.prev =  this.navgroup.addItem({
26907             tooltip: this.prevText,
26908             cls: "prev btn-outline-secondary",
26909             html : ' <i class="fa fa-backward"></i>',
26910             disabled: true,
26911             preventDefault: true,
26912             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26913         });
26914     //this.addSeparator();
26915         
26916         
26917         var field = this.navgroup.addItem( {
26918             tagtype : 'span',
26919             cls : 'x-paging-position  btn-outline-secondary',
26920              disabled: true,
26921             html : this.beforePageText  +
26922                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26923                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26924          } ); //?? escaped?
26925         
26926         this.field = field.el.select('input', true).first();
26927         this.field.on("keydown", this.onPagingKeydown, this);
26928         this.field.on("focus", function(){this.dom.select();});
26929     
26930     
26931         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26932         //this.field.setHeight(18);
26933         //this.addSeparator();
26934         this.next = this.navgroup.addItem({
26935             tooltip: this.nextText,
26936             cls: "next btn-outline-secondary",
26937             html : ' <i class="fa fa-forward"></i>',
26938             disabled: true,
26939             preventDefault: true,
26940             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26941         });
26942         this.last = this.navgroup.addItem({
26943             tooltip: this.lastText,
26944             html : ' <i class="fa fa-step-forward"></i>',
26945             cls: "next btn-outline-secondary",
26946             disabled: true,
26947             preventDefault: true,
26948             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26949         });
26950     //this.addSeparator();
26951         this.loading = this.navgroup.addItem({
26952             tooltip: this.refreshText,
26953             cls: "btn-outline-secondary",
26954             html : ' <i class="fa fa-refresh"></i>',
26955             preventDefault: true,
26956             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26957         });
26958         
26959     },
26960
26961     // private
26962     updateInfo : function(){
26963         if(this.displayEl){
26964             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26965             var msg = count == 0 ?
26966                 this.emptyMsg :
26967                 String.format(
26968                     this.displayMsg,
26969                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26970                 );
26971             this.displayEl.update(msg);
26972         }
26973     },
26974
26975     // private
26976     onLoad : function(ds, r, o)
26977     {
26978         this.cursor = o.params.start ? o.params.start : 0;
26979         
26980         var d = this.getPageData(),
26981             ap = d.activePage,
26982             ps = d.pages;
26983         
26984         
26985         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26986         this.field.dom.value = ap;
26987         this.first.setDisabled(ap == 1);
26988         this.prev.setDisabled(ap == 1);
26989         this.next.setDisabled(ap == ps);
26990         this.last.setDisabled(ap == ps);
26991         this.loading.enable();
26992         this.updateInfo();
26993     },
26994
26995     // private
26996     getPageData : function(){
26997         var total = this.ds.getTotalCount();
26998         return {
26999             total : total,
27000             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27001             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27002         };
27003     },
27004
27005     // private
27006     onLoadError : function(){
27007         this.loading.enable();
27008     },
27009
27010     // private
27011     onPagingKeydown : function(e){
27012         var k = e.getKey();
27013         var d = this.getPageData();
27014         if(k == e.RETURN){
27015             var v = this.field.dom.value, pageNum;
27016             if(!v || isNaN(pageNum = parseInt(v, 10))){
27017                 this.field.dom.value = d.activePage;
27018                 return;
27019             }
27020             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27021             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27022             e.stopEvent();
27023         }
27024         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))
27025         {
27026           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27027           this.field.dom.value = pageNum;
27028           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27029           e.stopEvent();
27030         }
27031         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27032         {
27033           var v = this.field.dom.value, pageNum; 
27034           var increment = (e.shiftKey) ? 10 : 1;
27035           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27036                 increment *= -1;
27037           }
27038           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27039             this.field.dom.value = d.activePage;
27040             return;
27041           }
27042           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27043           {
27044             this.field.dom.value = parseInt(v, 10) + increment;
27045             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27046             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27047           }
27048           e.stopEvent();
27049         }
27050     },
27051
27052     // private
27053     beforeLoad : function(){
27054         if(this.loading){
27055             this.loading.disable();
27056         }
27057     },
27058
27059     // private
27060     onClick : function(which){
27061         
27062         var ds = this.ds;
27063         if (!ds) {
27064             return;
27065         }
27066         
27067         switch(which){
27068             case "first":
27069                 ds.load({params:{start: 0, limit: this.pageSize}});
27070             break;
27071             case "prev":
27072                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27073             break;
27074             case "next":
27075                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27076             break;
27077             case "last":
27078                 var total = ds.getTotalCount();
27079                 var extra = total % this.pageSize;
27080                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27081                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27082             break;
27083             case "refresh":
27084                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27085             break;
27086         }
27087     },
27088
27089     /**
27090      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27091      * @param {Roo.data.Store} store The data store to unbind
27092      */
27093     unbind : function(ds){
27094         ds.un("beforeload", this.beforeLoad, this);
27095         ds.un("load", this.onLoad, this);
27096         ds.un("loadexception", this.onLoadError, this);
27097         ds.un("remove", this.updateInfo, this);
27098         ds.un("add", this.updateInfo, this);
27099         this.ds = undefined;
27100     },
27101
27102     /**
27103      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27104      * @param {Roo.data.Store} store The data store to bind
27105      */
27106     bind : function(ds){
27107         ds.on("beforeload", this.beforeLoad, this);
27108         ds.on("load", this.onLoad, this);
27109         ds.on("loadexception", this.onLoadError, this);
27110         ds.on("remove", this.updateInfo, this);
27111         ds.on("add", this.updateInfo, this);
27112         this.ds = ds;
27113     }
27114 });/*
27115  * - LGPL
27116  *
27117  * element
27118  * 
27119  */
27120
27121 /**
27122  * @class Roo.bootstrap.MessageBar
27123  * @extends Roo.bootstrap.Component
27124  * Bootstrap MessageBar class
27125  * @cfg {String} html contents of the MessageBar
27126  * @cfg {String} weight (info | success | warning | danger) default info
27127  * @cfg {String} beforeClass insert the bar before the given class
27128  * @cfg {Boolean} closable (true | false) default false
27129  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27130  * 
27131  * @constructor
27132  * Create a new Element
27133  * @param {Object} config The config object
27134  */
27135
27136 Roo.bootstrap.MessageBar = function(config){
27137     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27138 };
27139
27140 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27141     
27142     html: '',
27143     weight: 'info',
27144     closable: false,
27145     fixed: false,
27146     beforeClass: 'bootstrap-sticky-wrap',
27147     
27148     getAutoCreate : function(){
27149         
27150         var cfg = {
27151             tag: 'div',
27152             cls: 'alert alert-dismissable alert-' + this.weight,
27153             cn: [
27154                 {
27155                     tag: 'span',
27156                     cls: 'message',
27157                     html: this.html || ''
27158                 }
27159             ]
27160         };
27161         
27162         if(this.fixed){
27163             cfg.cls += ' alert-messages-fixed';
27164         }
27165         
27166         if(this.closable){
27167             cfg.cn.push({
27168                 tag: 'button',
27169                 cls: 'close',
27170                 html: 'x'
27171             });
27172         }
27173         
27174         return cfg;
27175     },
27176     
27177     onRender : function(ct, position)
27178     {
27179         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27180         
27181         if(!this.el){
27182             var cfg = Roo.apply({},  this.getAutoCreate());
27183             cfg.id = Roo.id();
27184             
27185             if (this.cls) {
27186                 cfg.cls += ' ' + this.cls;
27187             }
27188             if (this.style) {
27189                 cfg.style = this.style;
27190             }
27191             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27192             
27193             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27194         }
27195         
27196         this.el.select('>button.close').on('click', this.hide, this);
27197         
27198     },
27199     
27200     show : function()
27201     {
27202         if (!this.rendered) {
27203             this.render();
27204         }
27205         
27206         this.el.show();
27207         
27208         this.fireEvent('show', this);
27209         
27210     },
27211     
27212     hide : function()
27213     {
27214         if (!this.rendered) {
27215             this.render();
27216         }
27217         
27218         this.el.hide();
27219         
27220         this.fireEvent('hide', this);
27221     },
27222     
27223     update : function()
27224     {
27225 //        var e = this.el.dom.firstChild;
27226 //        
27227 //        if(this.closable){
27228 //            e = e.nextSibling;
27229 //        }
27230 //        
27231 //        e.data = this.html || '';
27232
27233         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27234     }
27235    
27236 });
27237
27238  
27239
27240      /*
27241  * - LGPL
27242  *
27243  * Graph
27244  * 
27245  */
27246
27247
27248 /**
27249  * @class Roo.bootstrap.Graph
27250  * @extends Roo.bootstrap.Component
27251  * Bootstrap Graph class
27252 > Prameters
27253  -sm {number} sm 4
27254  -md {number} md 5
27255  @cfg {String} graphtype  bar | vbar | pie
27256  @cfg {number} g_x coodinator | centre x (pie)
27257  @cfg {number} g_y coodinator | centre y (pie)
27258  @cfg {number} g_r radius (pie)
27259  @cfg {number} g_height height of the chart (respected by all elements in the set)
27260  @cfg {number} g_width width of the chart (respected by all elements in the set)
27261  @cfg {Object} title The title of the chart
27262     
27263  -{Array}  values
27264  -opts (object) options for the chart 
27265      o {
27266      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27267      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27268      o vgutter (number)
27269      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.
27270      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27271      o to
27272      o stretch (boolean)
27273      o }
27274  -opts (object) options for the pie
27275      o{
27276      o cut
27277      o startAngle (number)
27278      o endAngle (number)
27279      } 
27280  *
27281  * @constructor
27282  * Create a new Input
27283  * @param {Object} config The config object
27284  */
27285
27286 Roo.bootstrap.Graph = function(config){
27287     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27288     
27289     this.addEvents({
27290         // img events
27291         /**
27292          * @event click
27293          * The img click event for the img.
27294          * @param {Roo.EventObject} e
27295          */
27296         "click" : true
27297     });
27298 };
27299
27300 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27301     
27302     sm: 4,
27303     md: 5,
27304     graphtype: 'bar',
27305     g_height: 250,
27306     g_width: 400,
27307     g_x: 50,
27308     g_y: 50,
27309     g_r: 30,
27310     opts:{
27311         //g_colors: this.colors,
27312         g_type: 'soft',
27313         g_gutter: '20%'
27314
27315     },
27316     title : false,
27317
27318     getAutoCreate : function(){
27319         
27320         var cfg = {
27321             tag: 'div',
27322             html : null
27323         };
27324         
27325         
27326         return  cfg;
27327     },
27328
27329     onRender : function(ct,position){
27330         
27331         
27332         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27333         
27334         if (typeof(Raphael) == 'undefined') {
27335             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27336             return;
27337         }
27338         
27339         this.raphael = Raphael(this.el.dom);
27340         
27341                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27342                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27343                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27344                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27345                 /*
27346                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27347                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27348                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27349                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27350                 
27351                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27352                 r.barchart(330, 10, 300, 220, data1);
27353                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27354                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27355                 */
27356                 
27357                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27358                 // r.barchart(30, 30, 560, 250,  xdata, {
27359                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27360                 //     axis : "0 0 1 1",
27361                 //     axisxlabels :  xdata
27362                 //     //yvalues : cols,
27363                    
27364                 // });
27365 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27366 //        
27367 //        this.load(null,xdata,{
27368 //                axis : "0 0 1 1",
27369 //                axisxlabels :  xdata
27370 //                });
27371
27372     },
27373
27374     load : function(graphtype,xdata,opts)
27375     {
27376         this.raphael.clear();
27377         if(!graphtype) {
27378             graphtype = this.graphtype;
27379         }
27380         if(!opts){
27381             opts = this.opts;
27382         }
27383         var r = this.raphael,
27384             fin = function () {
27385                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27386             },
27387             fout = function () {
27388                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27389             },
27390             pfin = function() {
27391                 this.sector.stop();
27392                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27393
27394                 if (this.label) {
27395                     this.label[0].stop();
27396                     this.label[0].attr({ r: 7.5 });
27397                     this.label[1].attr({ "font-weight": 800 });
27398                 }
27399             },
27400             pfout = function() {
27401                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27402
27403                 if (this.label) {
27404                     this.label[0].animate({ r: 5 }, 500, "bounce");
27405                     this.label[1].attr({ "font-weight": 400 });
27406                 }
27407             };
27408
27409         switch(graphtype){
27410             case 'bar':
27411                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27412                 break;
27413             case 'hbar':
27414                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27415                 break;
27416             case 'pie':
27417 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27418 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27419 //            
27420                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27421                 
27422                 break;
27423
27424         }
27425         
27426         if(this.title){
27427             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27428         }
27429         
27430     },
27431     
27432     setTitle: function(o)
27433     {
27434         this.title = o;
27435     },
27436     
27437     initEvents: function() {
27438         
27439         if(!this.href){
27440             this.el.on('click', this.onClick, this);
27441         }
27442     },
27443     
27444     onClick : function(e)
27445     {
27446         Roo.log('img onclick');
27447         this.fireEvent('click', this, e);
27448     }
27449    
27450 });
27451
27452  
27453 /*
27454  * - LGPL
27455  *
27456  * numberBox
27457  * 
27458  */
27459 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27460
27461 /**
27462  * @class Roo.bootstrap.dash.NumberBox
27463  * @extends Roo.bootstrap.Component
27464  * Bootstrap NumberBox class
27465  * @cfg {String} headline Box headline
27466  * @cfg {String} content Box content
27467  * @cfg {String} icon Box icon
27468  * @cfg {String} footer Footer text
27469  * @cfg {String} fhref Footer href
27470  * 
27471  * @constructor
27472  * Create a new NumberBox
27473  * @param {Object} config The config object
27474  */
27475
27476
27477 Roo.bootstrap.dash.NumberBox = function(config){
27478     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27479     
27480 };
27481
27482 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27483     
27484     headline : '',
27485     content : '',
27486     icon : '',
27487     footer : '',
27488     fhref : '',
27489     ficon : '',
27490     
27491     getAutoCreate : function(){
27492         
27493         var cfg = {
27494             tag : 'div',
27495             cls : 'small-box ',
27496             cn : [
27497                 {
27498                     tag : 'div',
27499                     cls : 'inner',
27500                     cn :[
27501                         {
27502                             tag : 'h3',
27503                             cls : 'roo-headline',
27504                             html : this.headline
27505                         },
27506                         {
27507                             tag : 'p',
27508                             cls : 'roo-content',
27509                             html : this.content
27510                         }
27511                     ]
27512                 }
27513             ]
27514         };
27515         
27516         if(this.icon){
27517             cfg.cn.push({
27518                 tag : 'div',
27519                 cls : 'icon',
27520                 cn :[
27521                     {
27522                         tag : 'i',
27523                         cls : 'ion ' + this.icon
27524                     }
27525                 ]
27526             });
27527         }
27528         
27529         if(this.footer){
27530             var footer = {
27531                 tag : 'a',
27532                 cls : 'small-box-footer',
27533                 href : this.fhref || '#',
27534                 html : this.footer
27535             };
27536             
27537             cfg.cn.push(footer);
27538             
27539         }
27540         
27541         return  cfg;
27542     },
27543
27544     onRender : function(ct,position){
27545         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27546
27547
27548        
27549                 
27550     },
27551
27552     setHeadline: function (value)
27553     {
27554         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27555     },
27556     
27557     setFooter: function (value, href)
27558     {
27559         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27560         
27561         if(href){
27562             this.el.select('a.small-box-footer',true).first().attr('href', href);
27563         }
27564         
27565     },
27566
27567     setContent: function (value)
27568     {
27569         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27570     },
27571
27572     initEvents: function() 
27573     {   
27574         
27575     }
27576     
27577 });
27578
27579  
27580 /*
27581  * - LGPL
27582  *
27583  * TabBox
27584  * 
27585  */
27586 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27587
27588 /**
27589  * @class Roo.bootstrap.dash.TabBox
27590  * @extends Roo.bootstrap.Component
27591  * Bootstrap TabBox class
27592  * @cfg {String} title Title of the TabBox
27593  * @cfg {String} icon Icon of the TabBox
27594  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27595  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27596  * 
27597  * @constructor
27598  * Create a new TabBox
27599  * @param {Object} config The config object
27600  */
27601
27602
27603 Roo.bootstrap.dash.TabBox = function(config){
27604     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27605     this.addEvents({
27606         // raw events
27607         /**
27608          * @event addpane
27609          * When a pane is added
27610          * @param {Roo.bootstrap.dash.TabPane} pane
27611          */
27612         "addpane" : true,
27613         /**
27614          * @event activatepane
27615          * When a pane is activated
27616          * @param {Roo.bootstrap.dash.TabPane} pane
27617          */
27618         "activatepane" : true
27619         
27620          
27621     });
27622     
27623     this.panes = [];
27624 };
27625
27626 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27627
27628     title : '',
27629     icon : false,
27630     showtabs : true,
27631     tabScrollable : false,
27632     
27633     getChildContainer : function()
27634     {
27635         return this.el.select('.tab-content', true).first();
27636     },
27637     
27638     getAutoCreate : function(){
27639         
27640         var header = {
27641             tag: 'li',
27642             cls: 'pull-left header',
27643             html: this.title,
27644             cn : []
27645         };
27646         
27647         if(this.icon){
27648             header.cn.push({
27649                 tag: 'i',
27650                 cls: 'fa ' + this.icon
27651             });
27652         }
27653         
27654         var h = {
27655             tag: 'ul',
27656             cls: 'nav nav-tabs pull-right',
27657             cn: [
27658                 header
27659             ]
27660         };
27661         
27662         if(this.tabScrollable){
27663             h = {
27664                 tag: 'div',
27665                 cls: 'tab-header',
27666                 cn: [
27667                     {
27668                         tag: 'ul',
27669                         cls: 'nav nav-tabs pull-right',
27670                         cn: [
27671                             header
27672                         ]
27673                     }
27674                 ]
27675             };
27676         }
27677         
27678         var cfg = {
27679             tag: 'div',
27680             cls: 'nav-tabs-custom',
27681             cn: [
27682                 h,
27683                 {
27684                     tag: 'div',
27685                     cls: 'tab-content no-padding',
27686                     cn: []
27687                 }
27688             ]
27689         };
27690
27691         return  cfg;
27692     },
27693     initEvents : function()
27694     {
27695         //Roo.log('add add pane handler');
27696         this.on('addpane', this.onAddPane, this);
27697     },
27698      /**
27699      * Updates the box title
27700      * @param {String} html to set the title to.
27701      */
27702     setTitle : function(value)
27703     {
27704         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27705     },
27706     onAddPane : function(pane)
27707     {
27708         this.panes.push(pane);
27709         //Roo.log('addpane');
27710         //Roo.log(pane);
27711         // tabs are rendere left to right..
27712         if(!this.showtabs){
27713             return;
27714         }
27715         
27716         var ctr = this.el.select('.nav-tabs', true).first();
27717          
27718          
27719         var existing = ctr.select('.nav-tab',true);
27720         var qty = existing.getCount();;
27721         
27722         
27723         var tab = ctr.createChild({
27724             tag : 'li',
27725             cls : 'nav-tab' + (qty ? '' : ' active'),
27726             cn : [
27727                 {
27728                     tag : 'a',
27729                     href:'#',
27730                     html : pane.title
27731                 }
27732             ]
27733         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27734         pane.tab = tab;
27735         
27736         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27737         if (!qty) {
27738             pane.el.addClass('active');
27739         }
27740         
27741                 
27742     },
27743     onTabClick : function(ev,un,ob,pane)
27744     {
27745         //Roo.log('tab - prev default');
27746         ev.preventDefault();
27747         
27748         
27749         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27750         pane.tab.addClass('active');
27751         //Roo.log(pane.title);
27752         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27753         // technically we should have a deactivate event.. but maybe add later.
27754         // and it should not de-activate the selected tab...
27755         this.fireEvent('activatepane', pane);
27756         pane.el.addClass('active');
27757         pane.fireEvent('activate');
27758         
27759         
27760     },
27761     
27762     getActivePane : function()
27763     {
27764         var r = false;
27765         Roo.each(this.panes, function(p) {
27766             if(p.el.hasClass('active')){
27767                 r = p;
27768                 return false;
27769             }
27770             
27771             return;
27772         });
27773         
27774         return r;
27775     }
27776     
27777     
27778 });
27779
27780  
27781 /*
27782  * - LGPL
27783  *
27784  * Tab pane
27785  * 
27786  */
27787 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27788 /**
27789  * @class Roo.bootstrap.TabPane
27790  * @extends Roo.bootstrap.Component
27791  * Bootstrap TabPane class
27792  * @cfg {Boolean} active (false | true) Default false
27793  * @cfg {String} title title of panel
27794
27795  * 
27796  * @constructor
27797  * Create a new TabPane
27798  * @param {Object} config The config object
27799  */
27800
27801 Roo.bootstrap.dash.TabPane = function(config){
27802     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27803     
27804     this.addEvents({
27805         // raw events
27806         /**
27807          * @event activate
27808          * When a pane is activated
27809          * @param {Roo.bootstrap.dash.TabPane} pane
27810          */
27811         "activate" : true
27812          
27813     });
27814 };
27815
27816 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27817     
27818     active : false,
27819     title : '',
27820     
27821     // the tabBox that this is attached to.
27822     tab : false,
27823      
27824     getAutoCreate : function() 
27825     {
27826         var cfg = {
27827             tag: 'div',
27828             cls: 'tab-pane'
27829         };
27830         
27831         if(this.active){
27832             cfg.cls += ' active';
27833         }
27834         
27835         return cfg;
27836     },
27837     initEvents  : function()
27838     {
27839         //Roo.log('trigger add pane handler');
27840         this.parent().fireEvent('addpane', this)
27841     },
27842     
27843      /**
27844      * Updates the tab title 
27845      * @param {String} html to set the title to.
27846      */
27847     setTitle: function(str)
27848     {
27849         if (!this.tab) {
27850             return;
27851         }
27852         this.title = str;
27853         this.tab.select('a', true).first().dom.innerHTML = str;
27854         
27855     }
27856     
27857     
27858     
27859 });
27860
27861  
27862
27863
27864  /*
27865  * - LGPL
27866  *
27867  * menu
27868  * 
27869  */
27870 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27871
27872 /**
27873  * @class Roo.bootstrap.menu.Menu
27874  * @extends Roo.bootstrap.Component
27875  * Bootstrap Menu class - container for Menu
27876  * @cfg {String} html Text of the menu
27877  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27878  * @cfg {String} icon Font awesome icon
27879  * @cfg {String} pos Menu align to (top | bottom) default bottom
27880  * 
27881  * 
27882  * @constructor
27883  * Create a new Menu
27884  * @param {Object} config The config object
27885  */
27886
27887
27888 Roo.bootstrap.menu.Menu = function(config){
27889     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27890     
27891     this.addEvents({
27892         /**
27893          * @event beforeshow
27894          * Fires before this menu is displayed
27895          * @param {Roo.bootstrap.menu.Menu} this
27896          */
27897         beforeshow : true,
27898         /**
27899          * @event beforehide
27900          * Fires before this menu is hidden
27901          * @param {Roo.bootstrap.menu.Menu} this
27902          */
27903         beforehide : true,
27904         /**
27905          * @event show
27906          * Fires after this menu is displayed
27907          * @param {Roo.bootstrap.menu.Menu} this
27908          */
27909         show : true,
27910         /**
27911          * @event hide
27912          * Fires after this menu is hidden
27913          * @param {Roo.bootstrap.menu.Menu} this
27914          */
27915         hide : true,
27916         /**
27917          * @event click
27918          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27919          * @param {Roo.bootstrap.menu.Menu} this
27920          * @param {Roo.EventObject} e
27921          */
27922         click : true
27923     });
27924     
27925 };
27926
27927 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27928     
27929     submenu : false,
27930     html : '',
27931     weight : 'default',
27932     icon : false,
27933     pos : 'bottom',
27934     
27935     
27936     getChildContainer : function() {
27937         if(this.isSubMenu){
27938             return this.el;
27939         }
27940         
27941         return this.el.select('ul.dropdown-menu', true).first();  
27942     },
27943     
27944     getAutoCreate : function()
27945     {
27946         var text = [
27947             {
27948                 tag : 'span',
27949                 cls : 'roo-menu-text',
27950                 html : this.html
27951             }
27952         ];
27953         
27954         if(this.icon){
27955             text.unshift({
27956                 tag : 'i',
27957                 cls : 'fa ' + this.icon
27958             })
27959         }
27960         
27961         
27962         var cfg = {
27963             tag : 'div',
27964             cls : 'btn-group',
27965             cn : [
27966                 {
27967                     tag : 'button',
27968                     cls : 'dropdown-button btn btn-' + this.weight,
27969                     cn : text
27970                 },
27971                 {
27972                     tag : 'button',
27973                     cls : 'dropdown-toggle btn btn-' + this.weight,
27974                     cn : [
27975                         {
27976                             tag : 'span',
27977                             cls : 'caret'
27978                         }
27979                     ]
27980                 },
27981                 {
27982                     tag : 'ul',
27983                     cls : 'dropdown-menu'
27984                 }
27985             ]
27986             
27987         };
27988         
27989         if(this.pos == 'top'){
27990             cfg.cls += ' dropup';
27991         }
27992         
27993         if(this.isSubMenu){
27994             cfg = {
27995                 tag : 'ul',
27996                 cls : 'dropdown-menu'
27997             }
27998         }
27999         
28000         return cfg;
28001     },
28002     
28003     onRender : function(ct, position)
28004     {
28005         this.isSubMenu = ct.hasClass('dropdown-submenu');
28006         
28007         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28008     },
28009     
28010     initEvents : function() 
28011     {
28012         if(this.isSubMenu){
28013             return;
28014         }
28015         
28016         this.hidden = true;
28017         
28018         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28019         this.triggerEl.on('click', this.onTriggerPress, this);
28020         
28021         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28022         this.buttonEl.on('click', this.onClick, this);
28023         
28024     },
28025     
28026     list : function()
28027     {
28028         if(this.isSubMenu){
28029             return this.el;
28030         }
28031         
28032         return this.el.select('ul.dropdown-menu', true).first();
28033     },
28034     
28035     onClick : function(e)
28036     {
28037         this.fireEvent("click", this, e);
28038     },
28039     
28040     onTriggerPress  : function(e)
28041     {   
28042         if (this.isVisible()) {
28043             this.hide();
28044         } else {
28045             this.show();
28046         }
28047     },
28048     
28049     isVisible : function(){
28050         return !this.hidden;
28051     },
28052     
28053     show : function()
28054     {
28055         this.fireEvent("beforeshow", this);
28056         
28057         this.hidden = false;
28058         this.el.addClass('open');
28059         
28060         Roo.get(document).on("mouseup", this.onMouseUp, this);
28061         
28062         this.fireEvent("show", this);
28063         
28064         
28065     },
28066     
28067     hide : function()
28068     {
28069         this.fireEvent("beforehide", this);
28070         
28071         this.hidden = true;
28072         this.el.removeClass('open');
28073         
28074         Roo.get(document).un("mouseup", this.onMouseUp);
28075         
28076         this.fireEvent("hide", this);
28077     },
28078     
28079     onMouseUp : function()
28080     {
28081         this.hide();
28082     }
28083     
28084 });
28085
28086  
28087  /*
28088  * - LGPL
28089  *
28090  * menu item
28091  * 
28092  */
28093 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28094
28095 /**
28096  * @class Roo.bootstrap.menu.Item
28097  * @extends Roo.bootstrap.Component
28098  * Bootstrap MenuItem class
28099  * @cfg {Boolean} submenu (true | false) default false
28100  * @cfg {String} html text of the item
28101  * @cfg {String} href the link
28102  * @cfg {Boolean} disable (true | false) default false
28103  * @cfg {Boolean} preventDefault (true | false) default true
28104  * @cfg {String} icon Font awesome icon
28105  * @cfg {String} pos Submenu align to (left | right) default right 
28106  * 
28107  * 
28108  * @constructor
28109  * Create a new Item
28110  * @param {Object} config The config object
28111  */
28112
28113
28114 Roo.bootstrap.menu.Item = function(config){
28115     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28116     this.addEvents({
28117         /**
28118          * @event mouseover
28119          * Fires when the mouse is hovering over this menu
28120          * @param {Roo.bootstrap.menu.Item} this
28121          * @param {Roo.EventObject} e
28122          */
28123         mouseover : true,
28124         /**
28125          * @event mouseout
28126          * Fires when the mouse exits this menu
28127          * @param {Roo.bootstrap.menu.Item} this
28128          * @param {Roo.EventObject} e
28129          */
28130         mouseout : true,
28131         // raw events
28132         /**
28133          * @event click
28134          * The raw click event for the entire grid.
28135          * @param {Roo.EventObject} e
28136          */
28137         click : true
28138     });
28139 };
28140
28141 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28142     
28143     submenu : false,
28144     href : '',
28145     html : '',
28146     preventDefault: true,
28147     disable : false,
28148     icon : false,
28149     pos : 'right',
28150     
28151     getAutoCreate : function()
28152     {
28153         var text = [
28154             {
28155                 tag : 'span',
28156                 cls : 'roo-menu-item-text',
28157                 html : this.html
28158             }
28159         ];
28160         
28161         if(this.icon){
28162             text.unshift({
28163                 tag : 'i',
28164                 cls : 'fa ' + this.icon
28165             })
28166         }
28167         
28168         var cfg = {
28169             tag : 'li',
28170             cn : [
28171                 {
28172                     tag : 'a',
28173                     href : this.href || '#',
28174                     cn : text
28175                 }
28176             ]
28177         };
28178         
28179         if(this.disable){
28180             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28181         }
28182         
28183         if(this.submenu){
28184             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28185             
28186             if(this.pos == 'left'){
28187                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28188             }
28189         }
28190         
28191         return cfg;
28192     },
28193     
28194     initEvents : function() 
28195     {
28196         this.el.on('mouseover', this.onMouseOver, this);
28197         this.el.on('mouseout', this.onMouseOut, this);
28198         
28199         this.el.select('a', true).first().on('click', this.onClick, this);
28200         
28201     },
28202     
28203     onClick : function(e)
28204     {
28205         if(this.preventDefault){
28206             e.preventDefault();
28207         }
28208         
28209         this.fireEvent("click", this, e);
28210     },
28211     
28212     onMouseOver : function(e)
28213     {
28214         if(this.submenu && this.pos == 'left'){
28215             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28216         }
28217         
28218         this.fireEvent("mouseover", this, e);
28219     },
28220     
28221     onMouseOut : function(e)
28222     {
28223         this.fireEvent("mouseout", this, e);
28224     }
28225 });
28226
28227  
28228
28229  /*
28230  * - LGPL
28231  *
28232  * menu separator
28233  * 
28234  */
28235 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28236
28237 /**
28238  * @class Roo.bootstrap.menu.Separator
28239  * @extends Roo.bootstrap.Component
28240  * Bootstrap Separator class
28241  * 
28242  * @constructor
28243  * Create a new Separator
28244  * @param {Object} config The config object
28245  */
28246
28247
28248 Roo.bootstrap.menu.Separator = function(config){
28249     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28250 };
28251
28252 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28253     
28254     getAutoCreate : function(){
28255         var cfg = {
28256             tag : 'li',
28257             cls: 'divider'
28258         };
28259         
28260         return cfg;
28261     }
28262    
28263 });
28264
28265  
28266
28267  /*
28268  * - LGPL
28269  *
28270  * Tooltip
28271  * 
28272  */
28273
28274 /**
28275  * @class Roo.bootstrap.Tooltip
28276  * Bootstrap Tooltip class
28277  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28278  * to determine which dom element triggers the tooltip.
28279  * 
28280  * It needs to add support for additional attributes like tooltip-position
28281  * 
28282  * @constructor
28283  * Create a new Toolti
28284  * @param {Object} config The config object
28285  */
28286
28287 Roo.bootstrap.Tooltip = function(config){
28288     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28289     
28290     this.alignment = Roo.bootstrap.Tooltip.alignment;
28291     
28292     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28293         this.alignment = config.alignment;
28294     }
28295     
28296 };
28297
28298 Roo.apply(Roo.bootstrap.Tooltip, {
28299     /**
28300      * @function init initialize tooltip monitoring.
28301      * @static
28302      */
28303     currentEl : false,
28304     currentTip : false,
28305     currentRegion : false,
28306     
28307     //  init : delay?
28308     
28309     init : function()
28310     {
28311         Roo.get(document).on('mouseover', this.enter ,this);
28312         Roo.get(document).on('mouseout', this.leave, this);
28313          
28314         
28315         this.currentTip = new Roo.bootstrap.Tooltip();
28316     },
28317     
28318     enter : function(ev)
28319     {
28320         var dom = ev.getTarget();
28321         
28322         //Roo.log(['enter',dom]);
28323         var el = Roo.fly(dom);
28324         if (this.currentEl) {
28325             //Roo.log(dom);
28326             //Roo.log(this.currentEl);
28327             //Roo.log(this.currentEl.contains(dom));
28328             if (this.currentEl == el) {
28329                 return;
28330             }
28331             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28332                 return;
28333             }
28334
28335         }
28336         
28337         if (this.currentTip.el) {
28338             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28339         }    
28340         //Roo.log(ev);
28341         
28342         if(!el || el.dom == document){
28343             return;
28344         }
28345         
28346         var bindEl = el;
28347         
28348         // you can not look for children, as if el is the body.. then everythign is the child..
28349         if (!el.attr('tooltip')) { //
28350             if (!el.select("[tooltip]").elements.length) {
28351                 return;
28352             }
28353             // is the mouse over this child...?
28354             bindEl = el.select("[tooltip]").first();
28355             var xy = ev.getXY();
28356             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28357                 //Roo.log("not in region.");
28358                 return;
28359             }
28360             //Roo.log("child element over..");
28361             
28362         }
28363         this.currentEl = bindEl;
28364         this.currentTip.bind(bindEl);
28365         this.currentRegion = Roo.lib.Region.getRegion(dom);
28366         this.currentTip.enter();
28367         
28368     },
28369     leave : function(ev)
28370     {
28371         var dom = ev.getTarget();
28372         //Roo.log(['leave',dom]);
28373         if (!this.currentEl) {
28374             return;
28375         }
28376         
28377         
28378         if (dom != this.currentEl.dom) {
28379             return;
28380         }
28381         var xy = ev.getXY();
28382         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28383             return;
28384         }
28385         // only activate leave if mouse cursor is outside... bounding box..
28386         
28387         
28388         
28389         
28390         if (this.currentTip) {
28391             this.currentTip.leave();
28392         }
28393         //Roo.log('clear currentEl');
28394         this.currentEl = false;
28395         
28396         
28397     },
28398     alignment : {
28399         'left' : ['r-l', [-2,0], 'right'],
28400         'right' : ['l-r', [2,0], 'left'],
28401         'bottom' : ['t-b', [0,2], 'top'],
28402         'top' : [ 'b-t', [0,-2], 'bottom']
28403     }
28404     
28405 });
28406
28407
28408 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28409     
28410     
28411     bindEl : false,
28412     
28413     delay : null, // can be { show : 300 , hide: 500}
28414     
28415     timeout : null,
28416     
28417     hoverState : null, //???
28418     
28419     placement : 'bottom', 
28420     
28421     alignment : false,
28422     
28423     getAutoCreate : function(){
28424     
28425         var cfg = {
28426            cls : 'tooltip',   
28427            role : 'tooltip',
28428            cn : [
28429                 {
28430                     cls : 'tooltip-arrow arrow'
28431                 },
28432                 {
28433                     cls : 'tooltip-inner'
28434                 }
28435            ]
28436         };
28437         
28438         return cfg;
28439     },
28440     bind : function(el)
28441     {
28442         this.bindEl = el;
28443     },
28444     
28445     initEvents : function()
28446     {
28447         this.arrowEl = this.el.select('.arrow', true).first();
28448         this.innerEl = this.el.select('.tooltip-inner', true).first();
28449     },
28450     
28451     enter : function () {
28452        
28453         if (this.timeout != null) {
28454             clearTimeout(this.timeout);
28455         }
28456         
28457         this.hoverState = 'in';
28458          //Roo.log("enter - show");
28459         if (!this.delay || !this.delay.show) {
28460             this.show();
28461             return;
28462         }
28463         var _t = this;
28464         this.timeout = setTimeout(function () {
28465             if (_t.hoverState == 'in') {
28466                 _t.show();
28467             }
28468         }, this.delay.show);
28469     },
28470     leave : function()
28471     {
28472         clearTimeout(this.timeout);
28473     
28474         this.hoverState = 'out';
28475          if (!this.delay || !this.delay.hide) {
28476             this.hide();
28477             return;
28478         }
28479        
28480         var _t = this;
28481         this.timeout = setTimeout(function () {
28482             //Roo.log("leave - timeout");
28483             
28484             if (_t.hoverState == 'out') {
28485                 _t.hide();
28486                 Roo.bootstrap.Tooltip.currentEl = false;
28487             }
28488         }, delay);
28489     },
28490     
28491     show : function (msg)
28492     {
28493         if (!this.el) {
28494             this.render(document.body);
28495         }
28496         // set content.
28497         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28498         
28499         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28500         
28501         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28502         
28503         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28504                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28505         
28506         var placement = typeof this.placement == 'function' ?
28507             this.placement.call(this, this.el, on_el) :
28508             this.placement;
28509             
28510         var autoToken = /\s?auto?\s?/i;
28511         var autoPlace = autoToken.test(placement);
28512         if (autoPlace) {
28513             placement = placement.replace(autoToken, '') || 'top';
28514         }
28515         
28516         //this.el.detach()
28517         //this.el.setXY([0,0]);
28518         this.el.show();
28519         //this.el.dom.style.display='block';
28520         
28521         //this.el.appendTo(on_el);
28522         
28523         var p = this.getPosition();
28524         var box = this.el.getBox();
28525         
28526         if (autoPlace) {
28527             // fixme..
28528         }
28529         
28530         var align = this.alignment[placement];
28531         
28532         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28533         
28534         if(placement == 'top' || placement == 'bottom'){
28535             if(xy[0] < 0){
28536                 placement = 'right';
28537             }
28538             
28539             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28540                 placement = 'left';
28541             }
28542             
28543             var scroll = Roo.select('body', true).first().getScroll();
28544             
28545             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28546                 placement = 'top';
28547             }
28548             
28549             align = this.alignment[placement];
28550             
28551             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28552             
28553         }
28554         
28555         this.el.alignTo(this.bindEl, align[0],align[1]);
28556         //var arrow = this.el.select('.arrow',true).first();
28557         //arrow.set(align[2], 
28558         
28559         this.el.addClass(placement);
28560         this.el.addClass("bs-tooltip-"+ placement);
28561         
28562         this.el.addClass('in fade show');
28563         
28564         this.hoverState = null;
28565         
28566         if (this.el.hasClass('fade')) {
28567             // fade it?
28568         }
28569         
28570         
28571         
28572         
28573         
28574     },
28575     hide : function()
28576     {
28577          
28578         if (!this.el) {
28579             return;
28580         }
28581         //this.el.setXY([0,0]);
28582         this.el.removeClass(['show', 'in']);
28583         //this.el.hide();
28584         
28585     }
28586     
28587 });
28588  
28589
28590  /*
28591  * - LGPL
28592  *
28593  * Location Picker
28594  * 
28595  */
28596
28597 /**
28598  * @class Roo.bootstrap.LocationPicker
28599  * @extends Roo.bootstrap.Component
28600  * Bootstrap LocationPicker class
28601  * @cfg {Number} latitude Position when init default 0
28602  * @cfg {Number} longitude Position when init default 0
28603  * @cfg {Number} zoom default 15
28604  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28605  * @cfg {Boolean} mapTypeControl default false
28606  * @cfg {Boolean} disableDoubleClickZoom default false
28607  * @cfg {Boolean} scrollwheel default true
28608  * @cfg {Boolean} streetViewControl default false
28609  * @cfg {Number} radius default 0
28610  * @cfg {String} locationName
28611  * @cfg {Boolean} draggable default true
28612  * @cfg {Boolean} enableAutocomplete default false
28613  * @cfg {Boolean} enableReverseGeocode default true
28614  * @cfg {String} markerTitle
28615  * 
28616  * @constructor
28617  * Create a new LocationPicker
28618  * @param {Object} config The config object
28619  */
28620
28621
28622 Roo.bootstrap.LocationPicker = function(config){
28623     
28624     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28625     
28626     this.addEvents({
28627         /**
28628          * @event initial
28629          * Fires when the picker initialized.
28630          * @param {Roo.bootstrap.LocationPicker} this
28631          * @param {Google Location} location
28632          */
28633         initial : true,
28634         /**
28635          * @event positionchanged
28636          * Fires when the picker position changed.
28637          * @param {Roo.bootstrap.LocationPicker} this
28638          * @param {Google Location} location
28639          */
28640         positionchanged : true,
28641         /**
28642          * @event resize
28643          * Fires when the map resize.
28644          * @param {Roo.bootstrap.LocationPicker} this
28645          */
28646         resize : true,
28647         /**
28648          * @event show
28649          * Fires when the map show.
28650          * @param {Roo.bootstrap.LocationPicker} this
28651          */
28652         show : true,
28653         /**
28654          * @event hide
28655          * Fires when the map hide.
28656          * @param {Roo.bootstrap.LocationPicker} this
28657          */
28658         hide : true,
28659         /**
28660          * @event mapClick
28661          * Fires when click the map.
28662          * @param {Roo.bootstrap.LocationPicker} this
28663          * @param {Map event} e
28664          */
28665         mapClick : true,
28666         /**
28667          * @event mapRightClick
28668          * Fires when right click the map.
28669          * @param {Roo.bootstrap.LocationPicker} this
28670          * @param {Map event} e
28671          */
28672         mapRightClick : true,
28673         /**
28674          * @event markerClick
28675          * Fires when click the marker.
28676          * @param {Roo.bootstrap.LocationPicker} this
28677          * @param {Map event} e
28678          */
28679         markerClick : true,
28680         /**
28681          * @event markerRightClick
28682          * Fires when right click the marker.
28683          * @param {Roo.bootstrap.LocationPicker} this
28684          * @param {Map event} e
28685          */
28686         markerRightClick : true,
28687         /**
28688          * @event OverlayViewDraw
28689          * Fires when OverlayView Draw
28690          * @param {Roo.bootstrap.LocationPicker} this
28691          */
28692         OverlayViewDraw : true,
28693         /**
28694          * @event OverlayViewOnAdd
28695          * Fires when OverlayView Draw
28696          * @param {Roo.bootstrap.LocationPicker} this
28697          */
28698         OverlayViewOnAdd : true,
28699         /**
28700          * @event OverlayViewOnRemove
28701          * Fires when OverlayView Draw
28702          * @param {Roo.bootstrap.LocationPicker} this
28703          */
28704         OverlayViewOnRemove : true,
28705         /**
28706          * @event OverlayViewShow
28707          * Fires when OverlayView Draw
28708          * @param {Roo.bootstrap.LocationPicker} this
28709          * @param {Pixel} cpx
28710          */
28711         OverlayViewShow : true,
28712         /**
28713          * @event OverlayViewHide
28714          * Fires when OverlayView Draw
28715          * @param {Roo.bootstrap.LocationPicker} this
28716          */
28717         OverlayViewHide : true,
28718         /**
28719          * @event loadexception
28720          * Fires when load google lib failed.
28721          * @param {Roo.bootstrap.LocationPicker} this
28722          */
28723         loadexception : true
28724     });
28725         
28726 };
28727
28728 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28729     
28730     gMapContext: false,
28731     
28732     latitude: 0,
28733     longitude: 0,
28734     zoom: 15,
28735     mapTypeId: false,
28736     mapTypeControl: false,
28737     disableDoubleClickZoom: false,
28738     scrollwheel: true,
28739     streetViewControl: false,
28740     radius: 0,
28741     locationName: '',
28742     draggable: true,
28743     enableAutocomplete: false,
28744     enableReverseGeocode: true,
28745     markerTitle: '',
28746     
28747     getAutoCreate: function()
28748     {
28749
28750         var cfg = {
28751             tag: 'div',
28752             cls: 'roo-location-picker'
28753         };
28754         
28755         return cfg
28756     },
28757     
28758     initEvents: function(ct, position)
28759     {       
28760         if(!this.el.getWidth() || this.isApplied()){
28761             return;
28762         }
28763         
28764         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28765         
28766         this.initial();
28767     },
28768     
28769     initial: function()
28770     {
28771         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28772             this.fireEvent('loadexception', this);
28773             return;
28774         }
28775         
28776         if(!this.mapTypeId){
28777             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28778         }
28779         
28780         this.gMapContext = this.GMapContext();
28781         
28782         this.initOverlayView();
28783         
28784         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28785         
28786         var _this = this;
28787                 
28788         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28789             _this.setPosition(_this.gMapContext.marker.position);
28790         });
28791         
28792         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28793             _this.fireEvent('mapClick', this, event);
28794             
28795         });
28796
28797         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28798             _this.fireEvent('mapRightClick', this, event);
28799             
28800         });
28801         
28802         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28803             _this.fireEvent('markerClick', this, event);
28804             
28805         });
28806
28807         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28808             _this.fireEvent('markerRightClick', this, event);
28809             
28810         });
28811         
28812         this.setPosition(this.gMapContext.location);
28813         
28814         this.fireEvent('initial', this, this.gMapContext.location);
28815     },
28816     
28817     initOverlayView: function()
28818     {
28819         var _this = this;
28820         
28821         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28822             
28823             draw: function()
28824             {
28825                 _this.fireEvent('OverlayViewDraw', _this);
28826             },
28827             
28828             onAdd: function()
28829             {
28830                 _this.fireEvent('OverlayViewOnAdd', _this);
28831             },
28832             
28833             onRemove: function()
28834             {
28835                 _this.fireEvent('OverlayViewOnRemove', _this);
28836             },
28837             
28838             show: function(cpx)
28839             {
28840                 _this.fireEvent('OverlayViewShow', _this, cpx);
28841             },
28842             
28843             hide: function()
28844             {
28845                 _this.fireEvent('OverlayViewHide', _this);
28846             }
28847             
28848         });
28849     },
28850     
28851     fromLatLngToContainerPixel: function(event)
28852     {
28853         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28854     },
28855     
28856     isApplied: function() 
28857     {
28858         return this.getGmapContext() == false ? false : true;
28859     },
28860     
28861     getGmapContext: function() 
28862     {
28863         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28864     },
28865     
28866     GMapContext: function() 
28867     {
28868         var position = new google.maps.LatLng(this.latitude, this.longitude);
28869         
28870         var _map = new google.maps.Map(this.el.dom, {
28871             center: position,
28872             zoom: this.zoom,
28873             mapTypeId: this.mapTypeId,
28874             mapTypeControl: this.mapTypeControl,
28875             disableDoubleClickZoom: this.disableDoubleClickZoom,
28876             scrollwheel: this.scrollwheel,
28877             streetViewControl: this.streetViewControl,
28878             locationName: this.locationName,
28879             draggable: this.draggable,
28880             enableAutocomplete: this.enableAutocomplete,
28881             enableReverseGeocode: this.enableReverseGeocode
28882         });
28883         
28884         var _marker = new google.maps.Marker({
28885             position: position,
28886             map: _map,
28887             title: this.markerTitle,
28888             draggable: this.draggable
28889         });
28890         
28891         return {
28892             map: _map,
28893             marker: _marker,
28894             circle: null,
28895             location: position,
28896             radius: this.radius,
28897             locationName: this.locationName,
28898             addressComponents: {
28899                 formatted_address: null,
28900                 addressLine1: null,
28901                 addressLine2: null,
28902                 streetName: null,
28903                 streetNumber: null,
28904                 city: null,
28905                 district: null,
28906                 state: null,
28907                 stateOrProvince: null
28908             },
28909             settings: this,
28910             domContainer: this.el.dom,
28911             geodecoder: new google.maps.Geocoder()
28912         };
28913     },
28914     
28915     drawCircle: function(center, radius, options) 
28916     {
28917         if (this.gMapContext.circle != null) {
28918             this.gMapContext.circle.setMap(null);
28919         }
28920         if (radius > 0) {
28921             radius *= 1;
28922             options = Roo.apply({}, options, {
28923                 strokeColor: "#0000FF",
28924                 strokeOpacity: .35,
28925                 strokeWeight: 2,
28926                 fillColor: "#0000FF",
28927                 fillOpacity: .2
28928             });
28929             
28930             options.map = this.gMapContext.map;
28931             options.radius = radius;
28932             options.center = center;
28933             this.gMapContext.circle = new google.maps.Circle(options);
28934             return this.gMapContext.circle;
28935         }
28936         
28937         return null;
28938     },
28939     
28940     setPosition: function(location) 
28941     {
28942         this.gMapContext.location = location;
28943         this.gMapContext.marker.setPosition(location);
28944         this.gMapContext.map.panTo(location);
28945         this.drawCircle(location, this.gMapContext.radius, {});
28946         
28947         var _this = this;
28948         
28949         if (this.gMapContext.settings.enableReverseGeocode) {
28950             this.gMapContext.geodecoder.geocode({
28951                 latLng: this.gMapContext.location
28952             }, function(results, status) {
28953                 
28954                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28955                     _this.gMapContext.locationName = results[0].formatted_address;
28956                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28957                     
28958                     _this.fireEvent('positionchanged', this, location);
28959                 }
28960             });
28961             
28962             return;
28963         }
28964         
28965         this.fireEvent('positionchanged', this, location);
28966     },
28967     
28968     resize: function()
28969     {
28970         google.maps.event.trigger(this.gMapContext.map, "resize");
28971         
28972         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28973         
28974         this.fireEvent('resize', this);
28975     },
28976     
28977     setPositionByLatLng: function(latitude, longitude)
28978     {
28979         this.setPosition(new google.maps.LatLng(latitude, longitude));
28980     },
28981     
28982     getCurrentPosition: function() 
28983     {
28984         return {
28985             latitude: this.gMapContext.location.lat(),
28986             longitude: this.gMapContext.location.lng()
28987         };
28988     },
28989     
28990     getAddressName: function() 
28991     {
28992         return this.gMapContext.locationName;
28993     },
28994     
28995     getAddressComponents: function() 
28996     {
28997         return this.gMapContext.addressComponents;
28998     },
28999     
29000     address_component_from_google_geocode: function(address_components) 
29001     {
29002         var result = {};
29003         
29004         for (var i = 0; i < address_components.length; i++) {
29005             var component = address_components[i];
29006             if (component.types.indexOf("postal_code") >= 0) {
29007                 result.postalCode = component.short_name;
29008             } else if (component.types.indexOf("street_number") >= 0) {
29009                 result.streetNumber = component.short_name;
29010             } else if (component.types.indexOf("route") >= 0) {
29011                 result.streetName = component.short_name;
29012             } else if (component.types.indexOf("neighborhood") >= 0) {
29013                 result.city = component.short_name;
29014             } else if (component.types.indexOf("locality") >= 0) {
29015                 result.city = component.short_name;
29016             } else if (component.types.indexOf("sublocality") >= 0) {
29017                 result.district = component.short_name;
29018             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29019                 result.stateOrProvince = component.short_name;
29020             } else if (component.types.indexOf("country") >= 0) {
29021                 result.country = component.short_name;
29022             }
29023         }
29024         
29025         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29026         result.addressLine2 = "";
29027         return result;
29028     },
29029     
29030     setZoomLevel: function(zoom)
29031     {
29032         this.gMapContext.map.setZoom(zoom);
29033     },
29034     
29035     show: function()
29036     {
29037         if(!this.el){
29038             return;
29039         }
29040         
29041         this.el.show();
29042         
29043         this.resize();
29044         
29045         this.fireEvent('show', this);
29046     },
29047     
29048     hide: function()
29049     {
29050         if(!this.el){
29051             return;
29052         }
29053         
29054         this.el.hide();
29055         
29056         this.fireEvent('hide', this);
29057     }
29058     
29059 });
29060
29061 Roo.apply(Roo.bootstrap.LocationPicker, {
29062     
29063     OverlayView : function(map, options)
29064     {
29065         options = options || {};
29066         
29067         this.setMap(map);
29068     }
29069     
29070     
29071 });/**
29072  * @class Roo.bootstrap.Alert
29073  * @extends Roo.bootstrap.Component
29074  * Bootstrap Alert class - shows an alert area box
29075  * eg
29076  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29077   Enter a valid email address
29078 </div>
29079  * @licence LGPL
29080  * @cfg {String} title The title of alert
29081  * @cfg {String} html The content of alert
29082  * @cfg {String} weight (  success | info | warning | danger )
29083  * @cfg {String} faicon font-awesomeicon
29084  * 
29085  * @constructor
29086  * Create a new alert
29087  * @param {Object} config The config object
29088  */
29089
29090
29091 Roo.bootstrap.Alert = function(config){
29092     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29093     
29094 };
29095
29096 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29097     
29098     title: '',
29099     html: '',
29100     weight: false,
29101     faicon: false,
29102     
29103     getAutoCreate : function()
29104     {
29105         
29106         var cfg = {
29107             tag : 'div',
29108             cls : 'alert',
29109             cn : [
29110                 {
29111                     tag : 'i',
29112                     cls : 'roo-alert-icon'
29113                     
29114                 },
29115                 {
29116                     tag : 'b',
29117                     cls : 'roo-alert-title',
29118                     html : this.title
29119                 },
29120                 {
29121                     tag : 'span',
29122                     cls : 'roo-alert-text',
29123                     html : this.html
29124                 }
29125             ]
29126         };
29127         
29128         if(this.faicon){
29129             cfg.cn[0].cls += ' fa ' + this.faicon;
29130         }
29131         
29132         if(this.weight){
29133             cfg.cls += ' alert-' + this.weight;
29134         }
29135         
29136         return cfg;
29137     },
29138     
29139     initEvents: function() 
29140     {
29141         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29142     },
29143     
29144     setTitle : function(str)
29145     {
29146         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29147     },
29148     
29149     setText : function(str)
29150     {
29151         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29152     },
29153     
29154     setWeight : function(weight)
29155     {
29156         if(this.weight){
29157             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29158         }
29159         
29160         this.weight = weight;
29161         
29162         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29163     },
29164     
29165     setIcon : function(icon)
29166     {
29167         if(this.faicon){
29168             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29169         }
29170         
29171         this.faicon = icon;
29172         
29173         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29174     },
29175     
29176     hide: function() 
29177     {
29178         this.el.hide();   
29179     },
29180     
29181     show: function() 
29182     {  
29183         this.el.show();   
29184     }
29185     
29186 });
29187
29188  
29189 /*
29190 * Licence: LGPL
29191 */
29192
29193 /**
29194  * @class Roo.bootstrap.UploadCropbox
29195  * @extends Roo.bootstrap.Component
29196  * Bootstrap UploadCropbox class
29197  * @cfg {String} emptyText show when image has been loaded
29198  * @cfg {String} rotateNotify show when image too small to rotate
29199  * @cfg {Number} errorTimeout default 3000
29200  * @cfg {Number} minWidth default 300
29201  * @cfg {Number} minHeight default 300
29202  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29203  * @cfg {Boolean} isDocument (true|false) default false
29204  * @cfg {String} url action url
29205  * @cfg {String} paramName default 'imageUpload'
29206  * @cfg {String} method default POST
29207  * @cfg {Boolean} loadMask (true|false) default true
29208  * @cfg {Boolean} loadingText default 'Loading...'
29209  * 
29210  * @constructor
29211  * Create a new UploadCropbox
29212  * @param {Object} config The config object
29213  */
29214
29215 Roo.bootstrap.UploadCropbox = function(config){
29216     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29217     
29218     this.addEvents({
29219         /**
29220          * @event beforeselectfile
29221          * Fire before select file
29222          * @param {Roo.bootstrap.UploadCropbox} this
29223          */
29224         "beforeselectfile" : true,
29225         /**
29226          * @event initial
29227          * Fire after initEvent
29228          * @param {Roo.bootstrap.UploadCropbox} this
29229          */
29230         "initial" : true,
29231         /**
29232          * @event crop
29233          * Fire after initEvent
29234          * @param {Roo.bootstrap.UploadCropbox} this
29235          * @param {String} data
29236          */
29237         "crop" : true,
29238         /**
29239          * @event prepare
29240          * Fire when preparing the file data
29241          * @param {Roo.bootstrap.UploadCropbox} this
29242          * @param {Object} file
29243          */
29244         "prepare" : true,
29245         /**
29246          * @event exception
29247          * Fire when get exception
29248          * @param {Roo.bootstrap.UploadCropbox} this
29249          * @param {XMLHttpRequest} xhr
29250          */
29251         "exception" : true,
29252         /**
29253          * @event beforeloadcanvas
29254          * Fire before load the canvas
29255          * @param {Roo.bootstrap.UploadCropbox} this
29256          * @param {String} src
29257          */
29258         "beforeloadcanvas" : true,
29259         /**
29260          * @event trash
29261          * Fire when trash image
29262          * @param {Roo.bootstrap.UploadCropbox} this
29263          */
29264         "trash" : true,
29265         /**
29266          * @event download
29267          * Fire when download the image
29268          * @param {Roo.bootstrap.UploadCropbox} this
29269          */
29270         "download" : true,
29271         /**
29272          * @event footerbuttonclick
29273          * Fire when footerbuttonclick
29274          * @param {Roo.bootstrap.UploadCropbox} this
29275          * @param {String} type
29276          */
29277         "footerbuttonclick" : true,
29278         /**
29279          * @event resize
29280          * Fire when resize
29281          * @param {Roo.bootstrap.UploadCropbox} this
29282          */
29283         "resize" : true,
29284         /**
29285          * @event rotate
29286          * Fire when rotate the image
29287          * @param {Roo.bootstrap.UploadCropbox} this
29288          * @param {String} pos
29289          */
29290         "rotate" : true,
29291         /**
29292          * @event inspect
29293          * Fire when inspect the file
29294          * @param {Roo.bootstrap.UploadCropbox} this
29295          * @param {Object} file
29296          */
29297         "inspect" : true,
29298         /**
29299          * @event upload
29300          * Fire when xhr upload the file
29301          * @param {Roo.bootstrap.UploadCropbox} this
29302          * @param {Object} data
29303          */
29304         "upload" : true,
29305         /**
29306          * @event arrange
29307          * Fire when arrange the file data
29308          * @param {Roo.bootstrap.UploadCropbox} this
29309          * @param {Object} formData
29310          */
29311         "arrange" : true
29312     });
29313     
29314     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29315 };
29316
29317 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29318     
29319     emptyText : 'Click to upload image',
29320     rotateNotify : 'Image is too small to rotate',
29321     errorTimeout : 3000,
29322     scale : 0,
29323     baseScale : 1,
29324     rotate : 0,
29325     dragable : false,
29326     pinching : false,
29327     mouseX : 0,
29328     mouseY : 0,
29329     cropData : false,
29330     minWidth : 300,
29331     minHeight : 300,
29332     file : false,
29333     exif : {},
29334     baseRotate : 1,
29335     cropType : 'image/jpeg',
29336     buttons : false,
29337     canvasLoaded : false,
29338     isDocument : false,
29339     method : 'POST',
29340     paramName : 'imageUpload',
29341     loadMask : true,
29342     loadingText : 'Loading...',
29343     maskEl : false,
29344     
29345     getAutoCreate : function()
29346     {
29347         var cfg = {
29348             tag : 'div',
29349             cls : 'roo-upload-cropbox',
29350             cn : [
29351                 {
29352                     tag : 'input',
29353                     cls : 'roo-upload-cropbox-selector',
29354                     type : 'file'
29355                 },
29356                 {
29357                     tag : 'div',
29358                     cls : 'roo-upload-cropbox-body',
29359                     style : 'cursor:pointer',
29360                     cn : [
29361                         {
29362                             tag : 'div',
29363                             cls : 'roo-upload-cropbox-preview'
29364                         },
29365                         {
29366                             tag : 'div',
29367                             cls : 'roo-upload-cropbox-thumb'
29368                         },
29369                         {
29370                             tag : 'div',
29371                             cls : 'roo-upload-cropbox-empty-notify',
29372                             html : this.emptyText
29373                         },
29374                         {
29375                             tag : 'div',
29376                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29377                             html : this.rotateNotify
29378                         }
29379                     ]
29380                 },
29381                 {
29382                     tag : 'div',
29383                     cls : 'roo-upload-cropbox-footer',
29384                     cn : {
29385                         tag : 'div',
29386                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29387                         cn : []
29388                     }
29389                 }
29390             ]
29391         };
29392         
29393         return cfg;
29394     },
29395     
29396     onRender : function(ct, position)
29397     {
29398         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29399         
29400         if (this.buttons.length) {
29401             
29402             Roo.each(this.buttons, function(bb) {
29403                 
29404                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29405                 
29406                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29407                 
29408             }, this);
29409         }
29410         
29411         if(this.loadMask){
29412             this.maskEl = this.el;
29413         }
29414     },
29415     
29416     initEvents : function()
29417     {
29418         this.urlAPI = (window.createObjectURL && window) || 
29419                                 (window.URL && URL.revokeObjectURL && URL) || 
29420                                 (window.webkitURL && webkitURL);
29421                         
29422         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29423         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29424         
29425         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29426         this.selectorEl.hide();
29427         
29428         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29429         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29430         
29431         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29432         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29433         this.thumbEl.hide();
29434         
29435         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29436         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29437         
29438         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29439         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29440         this.errorEl.hide();
29441         
29442         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29443         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29444         this.footerEl.hide();
29445         
29446         this.setThumbBoxSize();
29447         
29448         this.bind();
29449         
29450         this.resize();
29451         
29452         this.fireEvent('initial', this);
29453     },
29454
29455     bind : function()
29456     {
29457         var _this = this;
29458         
29459         window.addEventListener("resize", function() { _this.resize(); } );
29460         
29461         this.bodyEl.on('click', this.beforeSelectFile, this);
29462         
29463         if(Roo.isTouch){
29464             this.bodyEl.on('touchstart', this.onTouchStart, this);
29465             this.bodyEl.on('touchmove', this.onTouchMove, this);
29466             this.bodyEl.on('touchend', this.onTouchEnd, this);
29467         }
29468         
29469         if(!Roo.isTouch){
29470             this.bodyEl.on('mousedown', this.onMouseDown, this);
29471             this.bodyEl.on('mousemove', this.onMouseMove, this);
29472             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29473             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29474             Roo.get(document).on('mouseup', this.onMouseUp, this);
29475         }
29476         
29477         this.selectorEl.on('change', this.onFileSelected, this);
29478     },
29479     
29480     reset : function()
29481     {    
29482         this.scale = 0;
29483         this.baseScale = 1;
29484         this.rotate = 0;
29485         this.baseRotate = 1;
29486         this.dragable = false;
29487         this.pinching = false;
29488         this.mouseX = 0;
29489         this.mouseY = 0;
29490         this.cropData = false;
29491         this.notifyEl.dom.innerHTML = this.emptyText;
29492         
29493         this.selectorEl.dom.value = '';
29494         
29495     },
29496     
29497     resize : function()
29498     {
29499         if(this.fireEvent('resize', this) != false){
29500             this.setThumbBoxPosition();
29501             this.setCanvasPosition();
29502         }
29503     },
29504     
29505     onFooterButtonClick : function(e, el, o, type)
29506     {
29507         switch (type) {
29508             case 'rotate-left' :
29509                 this.onRotateLeft(e);
29510                 break;
29511             case 'rotate-right' :
29512                 this.onRotateRight(e);
29513                 break;
29514             case 'picture' :
29515                 this.beforeSelectFile(e);
29516                 break;
29517             case 'trash' :
29518                 this.trash(e);
29519                 break;
29520             case 'crop' :
29521                 this.crop(e);
29522                 break;
29523             case 'download' :
29524                 this.download(e);
29525                 break;
29526             default :
29527                 break;
29528         }
29529         
29530         this.fireEvent('footerbuttonclick', this, type);
29531     },
29532     
29533     beforeSelectFile : function(e)
29534     {
29535         e.preventDefault();
29536         
29537         if(this.fireEvent('beforeselectfile', this) != false){
29538             this.selectorEl.dom.click();
29539         }
29540     },
29541     
29542     onFileSelected : function(e)
29543     {
29544         e.preventDefault();
29545         
29546         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29547             return;
29548         }
29549         
29550         var file = this.selectorEl.dom.files[0];
29551         
29552         if(this.fireEvent('inspect', this, file) != false){
29553             this.prepare(file);
29554         }
29555         
29556     },
29557     
29558     trash : function(e)
29559     {
29560         this.fireEvent('trash', this);
29561     },
29562     
29563     download : function(e)
29564     {
29565         this.fireEvent('download', this);
29566     },
29567     
29568     loadCanvas : function(src)
29569     {   
29570         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29571             
29572             this.reset();
29573             
29574             this.imageEl = document.createElement('img');
29575             
29576             var _this = this;
29577             
29578             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29579             
29580             this.imageEl.src = src;
29581         }
29582     },
29583     
29584     onLoadCanvas : function()
29585     {   
29586         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29587         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29588         
29589         this.bodyEl.un('click', this.beforeSelectFile, this);
29590         
29591         this.notifyEl.hide();
29592         this.thumbEl.show();
29593         this.footerEl.show();
29594         
29595         this.baseRotateLevel();
29596         
29597         if(this.isDocument){
29598             this.setThumbBoxSize();
29599         }
29600         
29601         this.setThumbBoxPosition();
29602         
29603         this.baseScaleLevel();
29604         
29605         this.draw();
29606         
29607         this.resize();
29608         
29609         this.canvasLoaded = true;
29610         
29611         if(this.loadMask){
29612             this.maskEl.unmask();
29613         }
29614         
29615     },
29616     
29617     setCanvasPosition : function()
29618     {   
29619         if(!this.canvasEl){
29620             return;
29621         }
29622         
29623         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29624         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29625         
29626         this.previewEl.setLeft(pw);
29627         this.previewEl.setTop(ph);
29628         
29629     },
29630     
29631     onMouseDown : function(e)
29632     {   
29633         e.stopEvent();
29634         
29635         this.dragable = true;
29636         this.pinching = false;
29637         
29638         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29639             this.dragable = false;
29640             return;
29641         }
29642         
29643         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29644         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29645         
29646     },
29647     
29648     onMouseMove : function(e)
29649     {   
29650         e.stopEvent();
29651         
29652         if(!this.canvasLoaded){
29653             return;
29654         }
29655         
29656         if (!this.dragable){
29657             return;
29658         }
29659         
29660         var minX = Math.ceil(this.thumbEl.getLeft(true));
29661         var minY = Math.ceil(this.thumbEl.getTop(true));
29662         
29663         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29664         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29665         
29666         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29667         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29668         
29669         x = x - this.mouseX;
29670         y = y - this.mouseY;
29671         
29672         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29673         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29674         
29675         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29676         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29677         
29678         this.previewEl.setLeft(bgX);
29679         this.previewEl.setTop(bgY);
29680         
29681         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29682         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29683     },
29684     
29685     onMouseUp : function(e)
29686     {   
29687         e.stopEvent();
29688         
29689         this.dragable = false;
29690     },
29691     
29692     onMouseWheel : function(e)
29693     {   
29694         e.stopEvent();
29695         
29696         this.startScale = this.scale;
29697         
29698         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29699         
29700         if(!this.zoomable()){
29701             this.scale = this.startScale;
29702             return;
29703         }
29704         
29705         this.draw();
29706         
29707         return;
29708     },
29709     
29710     zoomable : function()
29711     {
29712         var minScale = this.thumbEl.getWidth() / this.minWidth;
29713         
29714         if(this.minWidth < this.minHeight){
29715             minScale = this.thumbEl.getHeight() / this.minHeight;
29716         }
29717         
29718         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29719         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29720         
29721         if(
29722                 this.isDocument &&
29723                 (this.rotate == 0 || this.rotate == 180) && 
29724                 (
29725                     width > this.imageEl.OriginWidth || 
29726                     height > this.imageEl.OriginHeight ||
29727                     (width < this.minWidth && height < this.minHeight)
29728                 )
29729         ){
29730             return false;
29731         }
29732         
29733         if(
29734                 this.isDocument &&
29735                 (this.rotate == 90 || this.rotate == 270) && 
29736                 (
29737                     width > this.imageEl.OriginWidth || 
29738                     height > this.imageEl.OriginHeight ||
29739                     (width < this.minHeight && height < this.minWidth)
29740                 )
29741         ){
29742             return false;
29743         }
29744         
29745         if(
29746                 !this.isDocument &&
29747                 (this.rotate == 0 || this.rotate == 180) && 
29748                 (
29749                     width < this.minWidth || 
29750                     width > this.imageEl.OriginWidth || 
29751                     height < this.minHeight || 
29752                     height > this.imageEl.OriginHeight
29753                 )
29754         ){
29755             return false;
29756         }
29757         
29758         if(
29759                 !this.isDocument &&
29760                 (this.rotate == 90 || this.rotate == 270) && 
29761                 (
29762                     width < this.minHeight || 
29763                     width > this.imageEl.OriginWidth || 
29764                     height < this.minWidth || 
29765                     height > this.imageEl.OriginHeight
29766                 )
29767         ){
29768             return false;
29769         }
29770         
29771         return true;
29772         
29773     },
29774     
29775     onRotateLeft : function(e)
29776     {   
29777         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29778             
29779             var minScale = this.thumbEl.getWidth() / this.minWidth;
29780             
29781             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29782             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29783             
29784             this.startScale = this.scale;
29785             
29786             while (this.getScaleLevel() < minScale){
29787             
29788                 this.scale = this.scale + 1;
29789                 
29790                 if(!this.zoomable()){
29791                     break;
29792                 }
29793                 
29794                 if(
29795                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29796                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29797                 ){
29798                     continue;
29799                 }
29800                 
29801                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29802
29803                 this.draw();
29804                 
29805                 return;
29806             }
29807             
29808             this.scale = this.startScale;
29809             
29810             this.onRotateFail();
29811             
29812             return false;
29813         }
29814         
29815         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29816
29817         if(this.isDocument){
29818             this.setThumbBoxSize();
29819             this.setThumbBoxPosition();
29820             this.setCanvasPosition();
29821         }
29822         
29823         this.draw();
29824         
29825         this.fireEvent('rotate', this, 'left');
29826         
29827     },
29828     
29829     onRotateRight : function(e)
29830     {
29831         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29832             
29833             var minScale = this.thumbEl.getWidth() / this.minWidth;
29834         
29835             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29836             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29837             
29838             this.startScale = this.scale;
29839             
29840             while (this.getScaleLevel() < minScale){
29841             
29842                 this.scale = this.scale + 1;
29843                 
29844                 if(!this.zoomable()){
29845                     break;
29846                 }
29847                 
29848                 if(
29849                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29850                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29851                 ){
29852                     continue;
29853                 }
29854                 
29855                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29856
29857                 this.draw();
29858                 
29859                 return;
29860             }
29861             
29862             this.scale = this.startScale;
29863             
29864             this.onRotateFail();
29865             
29866             return false;
29867         }
29868         
29869         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29870
29871         if(this.isDocument){
29872             this.setThumbBoxSize();
29873             this.setThumbBoxPosition();
29874             this.setCanvasPosition();
29875         }
29876         
29877         this.draw();
29878         
29879         this.fireEvent('rotate', this, 'right');
29880     },
29881     
29882     onRotateFail : function()
29883     {
29884         this.errorEl.show(true);
29885         
29886         var _this = this;
29887         
29888         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29889     },
29890     
29891     draw : function()
29892     {
29893         this.previewEl.dom.innerHTML = '';
29894         
29895         var canvasEl = document.createElement("canvas");
29896         
29897         var contextEl = canvasEl.getContext("2d");
29898         
29899         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29900         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29901         var center = this.imageEl.OriginWidth / 2;
29902         
29903         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29904             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29905             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29906             center = this.imageEl.OriginHeight / 2;
29907         }
29908         
29909         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29910         
29911         contextEl.translate(center, center);
29912         contextEl.rotate(this.rotate * Math.PI / 180);
29913
29914         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29915         
29916         this.canvasEl = document.createElement("canvas");
29917         
29918         this.contextEl = this.canvasEl.getContext("2d");
29919         
29920         switch (this.rotate) {
29921             case 0 :
29922                 
29923                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29924                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29925                 
29926                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29927                 
29928                 break;
29929             case 90 : 
29930                 
29931                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29932                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29933                 
29934                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29935                     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);
29936                     break;
29937                 }
29938                 
29939                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29940                 
29941                 break;
29942             case 180 :
29943                 
29944                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29945                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29946                 
29947                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29948                     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);
29949                     break;
29950                 }
29951                 
29952                 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);
29953                 
29954                 break;
29955             case 270 :
29956                 
29957                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29958                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29959         
29960                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29961                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29962                     break;
29963                 }
29964                 
29965                 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);
29966                 
29967                 break;
29968             default : 
29969                 break;
29970         }
29971         
29972         this.previewEl.appendChild(this.canvasEl);
29973         
29974         this.setCanvasPosition();
29975     },
29976     
29977     crop : function()
29978     {
29979         if(!this.canvasLoaded){
29980             return;
29981         }
29982         
29983         var imageCanvas = document.createElement("canvas");
29984         
29985         var imageContext = imageCanvas.getContext("2d");
29986         
29987         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29988         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29989         
29990         var center = imageCanvas.width / 2;
29991         
29992         imageContext.translate(center, center);
29993         
29994         imageContext.rotate(this.rotate * Math.PI / 180);
29995         
29996         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29997         
29998         var canvas = document.createElement("canvas");
29999         
30000         var context = canvas.getContext("2d");
30001                 
30002         canvas.width = this.minWidth;
30003         canvas.height = this.minHeight;
30004
30005         switch (this.rotate) {
30006             case 0 :
30007                 
30008                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30009                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30010                 
30011                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30012                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30013                 
30014                 var targetWidth = this.minWidth - 2 * x;
30015                 var targetHeight = this.minHeight - 2 * y;
30016                 
30017                 var scale = 1;
30018                 
30019                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30020                     scale = targetWidth / width;
30021                 }
30022                 
30023                 if(x > 0 && y == 0){
30024                     scale = targetHeight / height;
30025                 }
30026                 
30027                 if(x > 0 && y > 0){
30028                     scale = targetWidth / width;
30029                     
30030                     if(width < height){
30031                         scale = targetHeight / height;
30032                     }
30033                 }
30034                 
30035                 context.scale(scale, scale);
30036                 
30037                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30038                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30039
30040                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30041                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30042
30043                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30044                 
30045                 break;
30046             case 90 : 
30047                 
30048                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30049                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30050                 
30051                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30052                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30053                 
30054                 var targetWidth = this.minWidth - 2 * x;
30055                 var targetHeight = this.minHeight - 2 * y;
30056                 
30057                 var scale = 1;
30058                 
30059                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30060                     scale = targetWidth / width;
30061                 }
30062                 
30063                 if(x > 0 && y == 0){
30064                     scale = targetHeight / height;
30065                 }
30066                 
30067                 if(x > 0 && y > 0){
30068                     scale = targetWidth / width;
30069                     
30070                     if(width < height){
30071                         scale = targetHeight / height;
30072                     }
30073                 }
30074                 
30075                 context.scale(scale, scale);
30076                 
30077                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30078                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30079
30080                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30081                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30082                 
30083                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30084                 
30085                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30086                 
30087                 break;
30088             case 180 :
30089                 
30090                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30091                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30092                 
30093                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30094                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30095                 
30096                 var targetWidth = this.minWidth - 2 * x;
30097                 var targetHeight = this.minHeight - 2 * y;
30098                 
30099                 var scale = 1;
30100                 
30101                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30102                     scale = targetWidth / width;
30103                 }
30104                 
30105                 if(x > 0 && y == 0){
30106                     scale = targetHeight / height;
30107                 }
30108                 
30109                 if(x > 0 && y > 0){
30110                     scale = targetWidth / width;
30111                     
30112                     if(width < height){
30113                         scale = targetHeight / height;
30114                     }
30115                 }
30116                 
30117                 context.scale(scale, scale);
30118                 
30119                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30120                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30121
30122                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30123                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30124
30125                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30126                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30127                 
30128                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30129                 
30130                 break;
30131             case 270 :
30132                 
30133                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30134                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30135                 
30136                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30137                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30138                 
30139                 var targetWidth = this.minWidth - 2 * x;
30140                 var targetHeight = this.minHeight - 2 * y;
30141                 
30142                 var scale = 1;
30143                 
30144                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30145                     scale = targetWidth / width;
30146                 }
30147                 
30148                 if(x > 0 && y == 0){
30149                     scale = targetHeight / height;
30150                 }
30151                 
30152                 if(x > 0 && y > 0){
30153                     scale = targetWidth / width;
30154                     
30155                     if(width < height){
30156                         scale = targetHeight / height;
30157                     }
30158                 }
30159                 
30160                 context.scale(scale, scale);
30161                 
30162                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30163                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30164
30165                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30166                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30167                 
30168                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30169                 
30170                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30171                 
30172                 break;
30173             default : 
30174                 break;
30175         }
30176         
30177         this.cropData = canvas.toDataURL(this.cropType);
30178         
30179         if(this.fireEvent('crop', this, this.cropData) !== false){
30180             this.process(this.file, this.cropData);
30181         }
30182         
30183         return;
30184         
30185     },
30186     
30187     setThumbBoxSize : function()
30188     {
30189         var width, height;
30190         
30191         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30192             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30193             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30194             
30195             this.minWidth = width;
30196             this.minHeight = height;
30197             
30198             if(this.rotate == 90 || this.rotate == 270){
30199                 this.minWidth = height;
30200                 this.minHeight = width;
30201             }
30202         }
30203         
30204         height = 300;
30205         width = Math.ceil(this.minWidth * height / this.minHeight);
30206         
30207         if(this.minWidth > this.minHeight){
30208             width = 300;
30209             height = Math.ceil(this.minHeight * width / this.minWidth);
30210         }
30211         
30212         this.thumbEl.setStyle({
30213             width : width + 'px',
30214             height : height + 'px'
30215         });
30216
30217         return;
30218             
30219     },
30220     
30221     setThumbBoxPosition : function()
30222     {
30223         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30224         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30225         
30226         this.thumbEl.setLeft(x);
30227         this.thumbEl.setTop(y);
30228         
30229     },
30230     
30231     baseRotateLevel : function()
30232     {
30233         this.baseRotate = 1;
30234         
30235         if(
30236                 typeof(this.exif) != 'undefined' &&
30237                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30238                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30239         ){
30240             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30241         }
30242         
30243         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30244         
30245     },
30246     
30247     baseScaleLevel : function()
30248     {
30249         var width, height;
30250         
30251         if(this.isDocument){
30252             
30253             if(this.baseRotate == 6 || this.baseRotate == 8){
30254             
30255                 height = this.thumbEl.getHeight();
30256                 this.baseScale = height / this.imageEl.OriginWidth;
30257
30258                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30259                     width = this.thumbEl.getWidth();
30260                     this.baseScale = width / this.imageEl.OriginHeight;
30261                 }
30262
30263                 return;
30264             }
30265
30266             height = this.thumbEl.getHeight();
30267             this.baseScale = height / this.imageEl.OriginHeight;
30268
30269             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30270                 width = this.thumbEl.getWidth();
30271                 this.baseScale = width / this.imageEl.OriginWidth;
30272             }
30273
30274             return;
30275         }
30276         
30277         if(this.baseRotate == 6 || this.baseRotate == 8){
30278             
30279             width = this.thumbEl.getHeight();
30280             this.baseScale = width / this.imageEl.OriginHeight;
30281             
30282             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30283                 height = this.thumbEl.getWidth();
30284                 this.baseScale = height / this.imageEl.OriginHeight;
30285             }
30286             
30287             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30288                 height = this.thumbEl.getWidth();
30289                 this.baseScale = height / this.imageEl.OriginHeight;
30290                 
30291                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30292                     width = this.thumbEl.getHeight();
30293                     this.baseScale = width / this.imageEl.OriginWidth;
30294                 }
30295             }
30296             
30297             return;
30298         }
30299         
30300         width = this.thumbEl.getWidth();
30301         this.baseScale = width / this.imageEl.OriginWidth;
30302         
30303         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30304             height = this.thumbEl.getHeight();
30305             this.baseScale = height / this.imageEl.OriginHeight;
30306         }
30307         
30308         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30309             
30310             height = this.thumbEl.getHeight();
30311             this.baseScale = height / this.imageEl.OriginHeight;
30312             
30313             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30314                 width = this.thumbEl.getWidth();
30315                 this.baseScale = width / this.imageEl.OriginWidth;
30316             }
30317             
30318         }
30319         
30320         return;
30321     },
30322     
30323     getScaleLevel : function()
30324     {
30325         return this.baseScale * Math.pow(1.1, this.scale);
30326     },
30327     
30328     onTouchStart : function(e)
30329     {
30330         if(!this.canvasLoaded){
30331             this.beforeSelectFile(e);
30332             return;
30333         }
30334         
30335         var touches = e.browserEvent.touches;
30336         
30337         if(!touches){
30338             return;
30339         }
30340         
30341         if(touches.length == 1){
30342             this.onMouseDown(e);
30343             return;
30344         }
30345         
30346         if(touches.length != 2){
30347             return;
30348         }
30349         
30350         var coords = [];
30351         
30352         for(var i = 0, finger; finger = touches[i]; i++){
30353             coords.push(finger.pageX, finger.pageY);
30354         }
30355         
30356         var x = Math.pow(coords[0] - coords[2], 2);
30357         var y = Math.pow(coords[1] - coords[3], 2);
30358         
30359         this.startDistance = Math.sqrt(x + y);
30360         
30361         this.startScale = this.scale;
30362         
30363         this.pinching = true;
30364         this.dragable = false;
30365         
30366     },
30367     
30368     onTouchMove : function(e)
30369     {
30370         if(!this.pinching && !this.dragable){
30371             return;
30372         }
30373         
30374         var touches = e.browserEvent.touches;
30375         
30376         if(!touches){
30377             return;
30378         }
30379         
30380         if(this.dragable){
30381             this.onMouseMove(e);
30382             return;
30383         }
30384         
30385         var coords = [];
30386         
30387         for(var i = 0, finger; finger = touches[i]; i++){
30388             coords.push(finger.pageX, finger.pageY);
30389         }
30390         
30391         var x = Math.pow(coords[0] - coords[2], 2);
30392         var y = Math.pow(coords[1] - coords[3], 2);
30393         
30394         this.endDistance = Math.sqrt(x + y);
30395         
30396         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30397         
30398         if(!this.zoomable()){
30399             this.scale = this.startScale;
30400             return;
30401         }
30402         
30403         this.draw();
30404         
30405     },
30406     
30407     onTouchEnd : function(e)
30408     {
30409         this.pinching = false;
30410         this.dragable = false;
30411         
30412     },
30413     
30414     process : function(file, crop)
30415     {
30416         if(this.loadMask){
30417             this.maskEl.mask(this.loadingText);
30418         }
30419         
30420         this.xhr = new XMLHttpRequest();
30421         
30422         file.xhr = this.xhr;
30423
30424         this.xhr.open(this.method, this.url, true);
30425         
30426         var headers = {
30427             "Accept": "application/json",
30428             "Cache-Control": "no-cache",
30429             "X-Requested-With": "XMLHttpRequest"
30430         };
30431         
30432         for (var headerName in headers) {
30433             var headerValue = headers[headerName];
30434             if (headerValue) {
30435                 this.xhr.setRequestHeader(headerName, headerValue);
30436             }
30437         }
30438         
30439         var _this = this;
30440         
30441         this.xhr.onload = function()
30442         {
30443             _this.xhrOnLoad(_this.xhr);
30444         }
30445         
30446         this.xhr.onerror = function()
30447         {
30448             _this.xhrOnError(_this.xhr);
30449         }
30450         
30451         var formData = new FormData();
30452
30453         formData.append('returnHTML', 'NO');
30454         
30455         if(crop){
30456             formData.append('crop', crop);
30457         }
30458         
30459         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30460             formData.append(this.paramName, file, file.name);
30461         }
30462         
30463         if(typeof(file.filename) != 'undefined'){
30464             formData.append('filename', file.filename);
30465         }
30466         
30467         if(typeof(file.mimetype) != 'undefined'){
30468             formData.append('mimetype', file.mimetype);
30469         }
30470         
30471         if(this.fireEvent('arrange', this, formData) != false){
30472             this.xhr.send(formData);
30473         };
30474     },
30475     
30476     xhrOnLoad : function(xhr)
30477     {
30478         if(this.loadMask){
30479             this.maskEl.unmask();
30480         }
30481         
30482         if (xhr.readyState !== 4) {
30483             this.fireEvent('exception', this, xhr);
30484             return;
30485         }
30486
30487         var response = Roo.decode(xhr.responseText);
30488         
30489         if(!response.success){
30490             this.fireEvent('exception', this, xhr);
30491             return;
30492         }
30493         
30494         var response = Roo.decode(xhr.responseText);
30495         
30496         this.fireEvent('upload', this, response);
30497         
30498     },
30499     
30500     xhrOnError : function()
30501     {
30502         if(this.loadMask){
30503             this.maskEl.unmask();
30504         }
30505         
30506         Roo.log('xhr on error');
30507         
30508         var response = Roo.decode(xhr.responseText);
30509           
30510         Roo.log(response);
30511         
30512     },
30513     
30514     prepare : function(file)
30515     {   
30516         if(this.loadMask){
30517             this.maskEl.mask(this.loadingText);
30518         }
30519         
30520         this.file = false;
30521         this.exif = {};
30522         
30523         if(typeof(file) === 'string'){
30524             this.loadCanvas(file);
30525             return;
30526         }
30527         
30528         if(!file || !this.urlAPI){
30529             return;
30530         }
30531         
30532         this.file = file;
30533         this.cropType = file.type;
30534         
30535         var _this = this;
30536         
30537         if(this.fireEvent('prepare', this, this.file) != false){
30538             
30539             var reader = new FileReader();
30540             
30541             reader.onload = function (e) {
30542                 if (e.target.error) {
30543                     Roo.log(e.target.error);
30544                     return;
30545                 }
30546                 
30547                 var buffer = e.target.result,
30548                     dataView = new DataView(buffer),
30549                     offset = 2,
30550                     maxOffset = dataView.byteLength - 4,
30551                     markerBytes,
30552                     markerLength;
30553                 
30554                 if (dataView.getUint16(0) === 0xffd8) {
30555                     while (offset < maxOffset) {
30556                         markerBytes = dataView.getUint16(offset);
30557                         
30558                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30559                             markerLength = dataView.getUint16(offset + 2) + 2;
30560                             if (offset + markerLength > dataView.byteLength) {
30561                                 Roo.log('Invalid meta data: Invalid segment size.');
30562                                 break;
30563                             }
30564                             
30565                             if(markerBytes == 0xffe1){
30566                                 _this.parseExifData(
30567                                     dataView,
30568                                     offset,
30569                                     markerLength
30570                                 );
30571                             }
30572                             
30573                             offset += markerLength;
30574                             
30575                             continue;
30576                         }
30577                         
30578                         break;
30579                     }
30580                     
30581                 }
30582                 
30583                 var url = _this.urlAPI.createObjectURL(_this.file);
30584                 
30585                 _this.loadCanvas(url);
30586                 
30587                 return;
30588             }
30589             
30590             reader.readAsArrayBuffer(this.file);
30591             
30592         }
30593         
30594     },
30595     
30596     parseExifData : function(dataView, offset, length)
30597     {
30598         var tiffOffset = offset + 10,
30599             littleEndian,
30600             dirOffset;
30601     
30602         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30603             // No Exif data, might be XMP data instead
30604             return;
30605         }
30606         
30607         // Check for the ASCII code for "Exif" (0x45786966):
30608         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30609             // No Exif data, might be XMP data instead
30610             return;
30611         }
30612         if (tiffOffset + 8 > dataView.byteLength) {
30613             Roo.log('Invalid Exif data: Invalid segment size.');
30614             return;
30615         }
30616         // Check for the two null bytes:
30617         if (dataView.getUint16(offset + 8) !== 0x0000) {
30618             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30619             return;
30620         }
30621         // Check the byte alignment:
30622         switch (dataView.getUint16(tiffOffset)) {
30623         case 0x4949:
30624             littleEndian = true;
30625             break;
30626         case 0x4D4D:
30627             littleEndian = false;
30628             break;
30629         default:
30630             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30631             return;
30632         }
30633         // Check for the TIFF tag marker (0x002A):
30634         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30635             Roo.log('Invalid Exif data: Missing TIFF marker.');
30636             return;
30637         }
30638         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30639         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30640         
30641         this.parseExifTags(
30642             dataView,
30643             tiffOffset,
30644             tiffOffset + dirOffset,
30645             littleEndian
30646         );
30647     },
30648     
30649     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30650     {
30651         var tagsNumber,
30652             dirEndOffset,
30653             i;
30654         if (dirOffset + 6 > dataView.byteLength) {
30655             Roo.log('Invalid Exif data: Invalid directory offset.');
30656             return;
30657         }
30658         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30659         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30660         if (dirEndOffset + 4 > dataView.byteLength) {
30661             Roo.log('Invalid Exif data: Invalid directory size.');
30662             return;
30663         }
30664         for (i = 0; i < tagsNumber; i += 1) {
30665             this.parseExifTag(
30666                 dataView,
30667                 tiffOffset,
30668                 dirOffset + 2 + 12 * i, // tag offset
30669                 littleEndian
30670             );
30671         }
30672         // Return the offset to the next directory:
30673         return dataView.getUint32(dirEndOffset, littleEndian);
30674     },
30675     
30676     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30677     {
30678         var tag = dataView.getUint16(offset, littleEndian);
30679         
30680         this.exif[tag] = this.getExifValue(
30681             dataView,
30682             tiffOffset,
30683             offset,
30684             dataView.getUint16(offset + 2, littleEndian), // tag type
30685             dataView.getUint32(offset + 4, littleEndian), // tag length
30686             littleEndian
30687         );
30688     },
30689     
30690     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30691     {
30692         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30693             tagSize,
30694             dataOffset,
30695             values,
30696             i,
30697             str,
30698             c;
30699     
30700         if (!tagType) {
30701             Roo.log('Invalid Exif data: Invalid tag type.');
30702             return;
30703         }
30704         
30705         tagSize = tagType.size * length;
30706         // Determine if the value is contained in the dataOffset bytes,
30707         // or if the value at the dataOffset is a pointer to the actual data:
30708         dataOffset = tagSize > 4 ?
30709                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30710         if (dataOffset + tagSize > dataView.byteLength) {
30711             Roo.log('Invalid Exif data: Invalid data offset.');
30712             return;
30713         }
30714         if (length === 1) {
30715             return tagType.getValue(dataView, dataOffset, littleEndian);
30716         }
30717         values = [];
30718         for (i = 0; i < length; i += 1) {
30719             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30720         }
30721         
30722         if (tagType.ascii) {
30723             str = '';
30724             // Concatenate the chars:
30725             for (i = 0; i < values.length; i += 1) {
30726                 c = values[i];
30727                 // Ignore the terminating NULL byte(s):
30728                 if (c === '\u0000') {
30729                     break;
30730                 }
30731                 str += c;
30732             }
30733             return str;
30734         }
30735         return values;
30736     }
30737     
30738 });
30739
30740 Roo.apply(Roo.bootstrap.UploadCropbox, {
30741     tags : {
30742         'Orientation': 0x0112
30743     },
30744     
30745     Orientation: {
30746             1: 0, //'top-left',
30747 //            2: 'top-right',
30748             3: 180, //'bottom-right',
30749 //            4: 'bottom-left',
30750 //            5: 'left-top',
30751             6: 90, //'right-top',
30752 //            7: 'right-bottom',
30753             8: 270 //'left-bottom'
30754     },
30755     
30756     exifTagTypes : {
30757         // byte, 8-bit unsigned int:
30758         1: {
30759             getValue: function (dataView, dataOffset) {
30760                 return dataView.getUint8(dataOffset);
30761             },
30762             size: 1
30763         },
30764         // ascii, 8-bit byte:
30765         2: {
30766             getValue: function (dataView, dataOffset) {
30767                 return String.fromCharCode(dataView.getUint8(dataOffset));
30768             },
30769             size: 1,
30770             ascii: true
30771         },
30772         // short, 16 bit int:
30773         3: {
30774             getValue: function (dataView, dataOffset, littleEndian) {
30775                 return dataView.getUint16(dataOffset, littleEndian);
30776             },
30777             size: 2
30778         },
30779         // long, 32 bit int:
30780         4: {
30781             getValue: function (dataView, dataOffset, littleEndian) {
30782                 return dataView.getUint32(dataOffset, littleEndian);
30783             },
30784             size: 4
30785         },
30786         // rational = two long values, first is numerator, second is denominator:
30787         5: {
30788             getValue: function (dataView, dataOffset, littleEndian) {
30789                 return dataView.getUint32(dataOffset, littleEndian) /
30790                     dataView.getUint32(dataOffset + 4, littleEndian);
30791             },
30792             size: 8
30793         },
30794         // slong, 32 bit signed int:
30795         9: {
30796             getValue: function (dataView, dataOffset, littleEndian) {
30797                 return dataView.getInt32(dataOffset, littleEndian);
30798             },
30799             size: 4
30800         },
30801         // srational, two slongs, first is numerator, second is denominator:
30802         10: {
30803             getValue: function (dataView, dataOffset, littleEndian) {
30804                 return dataView.getInt32(dataOffset, littleEndian) /
30805                     dataView.getInt32(dataOffset + 4, littleEndian);
30806             },
30807             size: 8
30808         }
30809     },
30810     
30811     footer : {
30812         STANDARD : [
30813             {
30814                 tag : 'div',
30815                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30816                 action : 'rotate-left',
30817                 cn : [
30818                     {
30819                         tag : 'button',
30820                         cls : 'btn btn-default',
30821                         html : '<i class="fa fa-undo"></i>'
30822                     }
30823                 ]
30824             },
30825             {
30826                 tag : 'div',
30827                 cls : 'btn-group roo-upload-cropbox-picture',
30828                 action : 'picture',
30829                 cn : [
30830                     {
30831                         tag : 'button',
30832                         cls : 'btn btn-default',
30833                         html : '<i class="fa fa-picture-o"></i>'
30834                     }
30835                 ]
30836             },
30837             {
30838                 tag : 'div',
30839                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30840                 action : 'rotate-right',
30841                 cn : [
30842                     {
30843                         tag : 'button',
30844                         cls : 'btn btn-default',
30845                         html : '<i class="fa fa-repeat"></i>'
30846                     }
30847                 ]
30848             }
30849         ],
30850         DOCUMENT : [
30851             {
30852                 tag : 'div',
30853                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30854                 action : 'rotate-left',
30855                 cn : [
30856                     {
30857                         tag : 'button',
30858                         cls : 'btn btn-default',
30859                         html : '<i class="fa fa-undo"></i>'
30860                     }
30861                 ]
30862             },
30863             {
30864                 tag : 'div',
30865                 cls : 'btn-group roo-upload-cropbox-download',
30866                 action : 'download',
30867                 cn : [
30868                     {
30869                         tag : 'button',
30870                         cls : 'btn btn-default',
30871                         html : '<i class="fa fa-download"></i>'
30872                     }
30873                 ]
30874             },
30875             {
30876                 tag : 'div',
30877                 cls : 'btn-group roo-upload-cropbox-crop',
30878                 action : 'crop',
30879                 cn : [
30880                     {
30881                         tag : 'button',
30882                         cls : 'btn btn-default',
30883                         html : '<i class="fa fa-crop"></i>'
30884                     }
30885                 ]
30886             },
30887             {
30888                 tag : 'div',
30889                 cls : 'btn-group roo-upload-cropbox-trash',
30890                 action : 'trash',
30891                 cn : [
30892                     {
30893                         tag : 'button',
30894                         cls : 'btn btn-default',
30895                         html : '<i class="fa fa-trash"></i>'
30896                     }
30897                 ]
30898             },
30899             {
30900                 tag : 'div',
30901                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30902                 action : 'rotate-right',
30903                 cn : [
30904                     {
30905                         tag : 'button',
30906                         cls : 'btn btn-default',
30907                         html : '<i class="fa fa-repeat"></i>'
30908                     }
30909                 ]
30910             }
30911         ],
30912         ROTATOR : [
30913             {
30914                 tag : 'div',
30915                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30916                 action : 'rotate-left',
30917                 cn : [
30918                     {
30919                         tag : 'button',
30920                         cls : 'btn btn-default',
30921                         html : '<i class="fa fa-undo"></i>'
30922                     }
30923                 ]
30924             },
30925             {
30926                 tag : 'div',
30927                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30928                 action : 'rotate-right',
30929                 cn : [
30930                     {
30931                         tag : 'button',
30932                         cls : 'btn btn-default',
30933                         html : '<i class="fa fa-repeat"></i>'
30934                     }
30935                 ]
30936             }
30937         ]
30938     }
30939 });
30940
30941 /*
30942 * Licence: LGPL
30943 */
30944
30945 /**
30946  * @class Roo.bootstrap.DocumentManager
30947  * @extends Roo.bootstrap.Component
30948  * Bootstrap DocumentManager class
30949  * @cfg {String} paramName default 'imageUpload'
30950  * @cfg {String} toolTipName default 'filename'
30951  * @cfg {String} method default POST
30952  * @cfg {String} url action url
30953  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30954  * @cfg {Boolean} multiple multiple upload default true
30955  * @cfg {Number} thumbSize default 300
30956  * @cfg {String} fieldLabel
30957  * @cfg {Number} labelWidth default 4
30958  * @cfg {String} labelAlign (left|top) default left
30959  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30960 * @cfg {Number} labellg set the width of label (1-12)
30961  * @cfg {Number} labelmd set the width of label (1-12)
30962  * @cfg {Number} labelsm set the width of label (1-12)
30963  * @cfg {Number} labelxs set the width of label (1-12)
30964  * 
30965  * @constructor
30966  * Create a new DocumentManager
30967  * @param {Object} config The config object
30968  */
30969
30970 Roo.bootstrap.DocumentManager = function(config){
30971     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30972     
30973     this.files = [];
30974     this.delegates = [];
30975     
30976     this.addEvents({
30977         /**
30978          * @event initial
30979          * Fire when initial the DocumentManager
30980          * @param {Roo.bootstrap.DocumentManager} this
30981          */
30982         "initial" : true,
30983         /**
30984          * @event inspect
30985          * inspect selected file
30986          * @param {Roo.bootstrap.DocumentManager} this
30987          * @param {File} file
30988          */
30989         "inspect" : true,
30990         /**
30991          * @event exception
30992          * Fire when xhr load exception
30993          * @param {Roo.bootstrap.DocumentManager} this
30994          * @param {XMLHttpRequest} xhr
30995          */
30996         "exception" : true,
30997         /**
30998          * @event afterupload
30999          * Fire when xhr load exception
31000          * @param {Roo.bootstrap.DocumentManager} this
31001          * @param {XMLHttpRequest} xhr
31002          */
31003         "afterupload" : true,
31004         /**
31005          * @event prepare
31006          * prepare the form data
31007          * @param {Roo.bootstrap.DocumentManager} this
31008          * @param {Object} formData
31009          */
31010         "prepare" : true,
31011         /**
31012          * @event remove
31013          * Fire when remove the file
31014          * @param {Roo.bootstrap.DocumentManager} this
31015          * @param {Object} file
31016          */
31017         "remove" : true,
31018         /**
31019          * @event refresh
31020          * Fire after refresh the file
31021          * @param {Roo.bootstrap.DocumentManager} this
31022          */
31023         "refresh" : true,
31024         /**
31025          * @event click
31026          * Fire after click the image
31027          * @param {Roo.bootstrap.DocumentManager} this
31028          * @param {Object} file
31029          */
31030         "click" : true,
31031         /**
31032          * @event edit
31033          * Fire when upload a image and editable set to true
31034          * @param {Roo.bootstrap.DocumentManager} this
31035          * @param {Object} file
31036          */
31037         "edit" : true,
31038         /**
31039          * @event beforeselectfile
31040          * Fire before select file
31041          * @param {Roo.bootstrap.DocumentManager} this
31042          */
31043         "beforeselectfile" : true,
31044         /**
31045          * @event process
31046          * Fire before process file
31047          * @param {Roo.bootstrap.DocumentManager} this
31048          * @param {Object} file
31049          */
31050         "process" : true,
31051         /**
31052          * @event previewrendered
31053          * Fire when preview rendered
31054          * @param {Roo.bootstrap.DocumentManager} this
31055          * @param {Object} file
31056          */
31057         "previewrendered" : true,
31058         /**
31059          */
31060         "previewResize" : true
31061         
31062     });
31063 };
31064
31065 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31066     
31067     boxes : 0,
31068     inputName : '',
31069     thumbSize : 300,
31070     multiple : true,
31071     files : false,
31072     method : 'POST',
31073     url : '',
31074     paramName : 'imageUpload',
31075     toolTipName : 'filename',
31076     fieldLabel : '',
31077     labelWidth : 4,
31078     labelAlign : 'left',
31079     editable : true,
31080     delegates : false,
31081     xhr : false, 
31082     
31083     labellg : 0,
31084     labelmd : 0,
31085     labelsm : 0,
31086     labelxs : 0,
31087     
31088     getAutoCreate : function()
31089     {   
31090         var managerWidget = {
31091             tag : 'div',
31092             cls : 'roo-document-manager',
31093             cn : [
31094                 {
31095                     tag : 'input',
31096                     cls : 'roo-document-manager-selector',
31097                     type : 'file'
31098                 },
31099                 {
31100                     tag : 'div',
31101                     cls : 'roo-document-manager-uploader',
31102                     cn : [
31103                         {
31104                             tag : 'div',
31105                             cls : 'roo-document-manager-upload-btn',
31106                             html : '<i class="fa fa-plus"></i>'
31107                         }
31108                     ]
31109                     
31110                 }
31111             ]
31112         };
31113         
31114         var content = [
31115             {
31116                 tag : 'div',
31117                 cls : 'column col-md-12',
31118                 cn : managerWidget
31119             }
31120         ];
31121         
31122         if(this.fieldLabel.length){
31123             
31124             content = [
31125                 {
31126                     tag : 'div',
31127                     cls : 'column col-md-12',
31128                     html : this.fieldLabel
31129                 },
31130                 {
31131                     tag : 'div',
31132                     cls : 'column col-md-12',
31133                     cn : managerWidget
31134                 }
31135             ];
31136
31137             if(this.labelAlign == 'left'){
31138                 content = [
31139                     {
31140                         tag : 'div',
31141                         cls : 'column',
31142                         html : this.fieldLabel
31143                     },
31144                     {
31145                         tag : 'div',
31146                         cls : 'column',
31147                         cn : managerWidget
31148                     }
31149                 ];
31150                 
31151                 if(this.labelWidth > 12){
31152                     content[0].style = "width: " + this.labelWidth + 'px';
31153                 }
31154
31155                 if(this.labelWidth < 13 && this.labelmd == 0){
31156                     this.labelmd = this.labelWidth;
31157                 }
31158
31159                 if(this.labellg > 0){
31160                     content[0].cls += ' col-lg-' + this.labellg;
31161                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31162                 }
31163
31164                 if(this.labelmd > 0){
31165                     content[0].cls += ' col-md-' + this.labelmd;
31166                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31167                 }
31168
31169                 if(this.labelsm > 0){
31170                     content[0].cls += ' col-sm-' + this.labelsm;
31171                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31172                 }
31173
31174                 if(this.labelxs > 0){
31175                     content[0].cls += ' col-xs-' + this.labelxs;
31176                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31177                 }
31178                 
31179             }
31180         }
31181         
31182         var cfg = {
31183             tag : 'div',
31184             cls : 'row clearfix',
31185             cn : content
31186         };
31187         
31188         return cfg;
31189         
31190     },
31191     
31192     initEvents : function()
31193     {
31194         this.managerEl = this.el.select('.roo-document-manager', true).first();
31195         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31196         
31197         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31198         this.selectorEl.hide();
31199         
31200         if(this.multiple){
31201             this.selectorEl.attr('multiple', 'multiple');
31202         }
31203         
31204         this.selectorEl.on('change', this.onFileSelected, this);
31205         
31206         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31207         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31208         
31209         this.uploader.on('click', this.onUploaderClick, this);
31210         
31211         this.renderProgressDialog();
31212         
31213         var _this = this;
31214         
31215         window.addEventListener("resize", function() { _this.refresh(); } );
31216         
31217         this.fireEvent('initial', this);
31218     },
31219     
31220     renderProgressDialog : function()
31221     {
31222         var _this = this;
31223         
31224         this.progressDialog = new Roo.bootstrap.Modal({
31225             cls : 'roo-document-manager-progress-dialog',
31226             allow_close : false,
31227             animate : false,
31228             title : '',
31229             buttons : [
31230                 {
31231                     name  :'cancel',
31232                     weight : 'danger',
31233                     html : 'Cancel'
31234                 }
31235             ], 
31236             listeners : { 
31237                 btnclick : function() {
31238                     _this.uploadCancel();
31239                     this.hide();
31240                 }
31241             }
31242         });
31243          
31244         this.progressDialog.render(Roo.get(document.body));
31245          
31246         this.progress = new Roo.bootstrap.Progress({
31247             cls : 'roo-document-manager-progress',
31248             active : true,
31249             striped : true
31250         });
31251         
31252         this.progress.render(this.progressDialog.getChildContainer());
31253         
31254         this.progressBar = new Roo.bootstrap.ProgressBar({
31255             cls : 'roo-document-manager-progress-bar',
31256             aria_valuenow : 0,
31257             aria_valuemin : 0,
31258             aria_valuemax : 12,
31259             panel : 'success'
31260         });
31261         
31262         this.progressBar.render(this.progress.getChildContainer());
31263     },
31264     
31265     onUploaderClick : function(e)
31266     {
31267         e.preventDefault();
31268      
31269         if(this.fireEvent('beforeselectfile', this) != false){
31270             this.selectorEl.dom.click();
31271         }
31272         
31273     },
31274     
31275     onFileSelected : function(e)
31276     {
31277         e.preventDefault();
31278         
31279         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31280             return;
31281         }
31282         
31283         Roo.each(this.selectorEl.dom.files, function(file){
31284             if(this.fireEvent('inspect', this, file) != false){
31285                 this.files.push(file);
31286             }
31287         }, this);
31288         
31289         this.queue();
31290         
31291     },
31292     
31293     queue : function()
31294     {
31295         this.selectorEl.dom.value = '';
31296         
31297         if(!this.files || !this.files.length){
31298             return;
31299         }
31300         
31301         if(this.boxes > 0 && this.files.length > this.boxes){
31302             this.files = this.files.slice(0, this.boxes);
31303         }
31304         
31305         this.uploader.show();
31306         
31307         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31308             this.uploader.hide();
31309         }
31310         
31311         var _this = this;
31312         
31313         var files = [];
31314         
31315         var docs = [];
31316         
31317         Roo.each(this.files, function(file){
31318             
31319             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31320                 var f = this.renderPreview(file);
31321                 files.push(f);
31322                 return;
31323             }
31324             
31325             if(file.type.indexOf('image') != -1){
31326                 this.delegates.push(
31327                     (function(){
31328                         _this.process(file);
31329                     }).createDelegate(this)
31330                 );
31331         
31332                 return;
31333             }
31334             
31335             docs.push(
31336                 (function(){
31337                     _this.process(file);
31338                 }).createDelegate(this)
31339             );
31340             
31341         }, this);
31342         
31343         this.files = files;
31344         
31345         this.delegates = this.delegates.concat(docs);
31346         
31347         if(!this.delegates.length){
31348             this.refresh();
31349             return;
31350         }
31351         
31352         this.progressBar.aria_valuemax = this.delegates.length;
31353         
31354         this.arrange();
31355         
31356         return;
31357     },
31358     
31359     arrange : function()
31360     {
31361         if(!this.delegates.length){
31362             this.progressDialog.hide();
31363             this.refresh();
31364             return;
31365         }
31366         
31367         var delegate = this.delegates.shift();
31368         
31369         this.progressDialog.show();
31370         
31371         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31372         
31373         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31374         
31375         delegate();
31376     },
31377     
31378     refresh : function()
31379     {
31380         this.uploader.show();
31381         
31382         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31383             this.uploader.hide();
31384         }
31385         
31386         Roo.isTouch ? this.closable(false) : this.closable(true);
31387         
31388         this.fireEvent('refresh', this);
31389     },
31390     
31391     onRemove : function(e, el, o)
31392     {
31393         e.preventDefault();
31394         
31395         this.fireEvent('remove', this, o);
31396         
31397     },
31398     
31399     remove : function(o)
31400     {
31401         var files = [];
31402         
31403         Roo.each(this.files, function(file){
31404             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31405                 files.push(file);
31406                 return;
31407             }
31408
31409             o.target.remove();
31410
31411         }, this);
31412         
31413         this.files = files;
31414         
31415         this.refresh();
31416     },
31417     
31418     clear : function()
31419     {
31420         Roo.each(this.files, function(file){
31421             if(!file.target){
31422                 return;
31423             }
31424             
31425             file.target.remove();
31426
31427         }, this);
31428         
31429         this.files = [];
31430         
31431         this.refresh();
31432     },
31433     
31434     onClick : function(e, el, o)
31435     {
31436         e.preventDefault();
31437         
31438         this.fireEvent('click', this, o);
31439         
31440     },
31441     
31442     closable : function(closable)
31443     {
31444         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31445             
31446             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31447             
31448             if(closable){
31449                 el.show();
31450                 return;
31451             }
31452             
31453             el.hide();
31454             
31455         }, this);
31456     },
31457     
31458     xhrOnLoad : function(xhr)
31459     {
31460         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31461             el.remove();
31462         }, this);
31463         
31464         if (xhr.readyState !== 4) {
31465             this.arrange();
31466             this.fireEvent('exception', this, xhr);
31467             return;
31468         }
31469
31470         var response = Roo.decode(xhr.responseText);
31471         
31472         if(!response.success){
31473             this.arrange();
31474             this.fireEvent('exception', this, xhr);
31475             return;
31476         }
31477         
31478         var file = this.renderPreview(response.data);
31479         
31480         this.files.push(file);
31481         
31482         this.arrange();
31483         
31484         this.fireEvent('afterupload', this, xhr);
31485         
31486     },
31487     
31488     xhrOnError : function(xhr)
31489     {
31490         Roo.log('xhr on error');
31491         
31492         var response = Roo.decode(xhr.responseText);
31493           
31494         Roo.log(response);
31495         
31496         this.arrange();
31497     },
31498     
31499     process : function(file)
31500     {
31501         if(this.fireEvent('process', this, file) !== false){
31502             if(this.editable && file.type.indexOf('image') != -1){
31503                 this.fireEvent('edit', this, file);
31504                 return;
31505             }
31506
31507             this.uploadStart(file, false);
31508
31509             return;
31510         }
31511         
31512     },
31513     
31514     uploadStart : function(file, crop)
31515     {
31516         this.xhr = new XMLHttpRequest();
31517         
31518         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31519             this.arrange();
31520             return;
31521         }
31522         
31523         file.xhr = this.xhr;
31524             
31525         this.managerEl.createChild({
31526             tag : 'div',
31527             cls : 'roo-document-manager-loading',
31528             cn : [
31529                 {
31530                     tag : 'div',
31531                     tooltip : file.name,
31532                     cls : 'roo-document-manager-thumb',
31533                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31534                 }
31535             ]
31536
31537         });
31538
31539         this.xhr.open(this.method, this.url, true);
31540         
31541         var headers = {
31542             "Accept": "application/json",
31543             "Cache-Control": "no-cache",
31544             "X-Requested-With": "XMLHttpRequest"
31545         };
31546         
31547         for (var headerName in headers) {
31548             var headerValue = headers[headerName];
31549             if (headerValue) {
31550                 this.xhr.setRequestHeader(headerName, headerValue);
31551             }
31552         }
31553         
31554         var _this = this;
31555         
31556         this.xhr.onload = function()
31557         {
31558             _this.xhrOnLoad(_this.xhr);
31559         }
31560         
31561         this.xhr.onerror = function()
31562         {
31563             _this.xhrOnError(_this.xhr);
31564         }
31565         
31566         var formData = new FormData();
31567
31568         formData.append('returnHTML', 'NO');
31569         
31570         if(crop){
31571             formData.append('crop', crop);
31572         }
31573         
31574         formData.append(this.paramName, file, file.name);
31575         
31576         var options = {
31577             file : file, 
31578             manually : false
31579         };
31580         
31581         if(this.fireEvent('prepare', this, formData, options) != false){
31582             
31583             if(options.manually){
31584                 return;
31585             }
31586             
31587             this.xhr.send(formData);
31588             return;
31589         };
31590         
31591         this.uploadCancel();
31592     },
31593     
31594     uploadCancel : function()
31595     {
31596         if (this.xhr) {
31597             this.xhr.abort();
31598         }
31599         
31600         this.delegates = [];
31601         
31602         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31603             el.remove();
31604         }, this);
31605         
31606         this.arrange();
31607     },
31608     
31609     renderPreview : function(file)
31610     {
31611         if(typeof(file.target) != 'undefined' && file.target){
31612             return file;
31613         }
31614         
31615         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31616         
31617         var previewEl = this.managerEl.createChild({
31618             tag : 'div',
31619             cls : 'roo-document-manager-preview',
31620             cn : [
31621                 {
31622                     tag : 'div',
31623                     tooltip : file[this.toolTipName],
31624                     cls : 'roo-document-manager-thumb',
31625                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31626                 },
31627                 {
31628                     tag : 'button',
31629                     cls : 'close',
31630                     html : '<i class="fa fa-times-circle"></i>'
31631                 }
31632             ]
31633         });
31634
31635         var close = previewEl.select('button.close', true).first();
31636
31637         close.on('click', this.onRemove, this, file);
31638
31639         file.target = previewEl;
31640
31641         var image = previewEl.select('img', true).first();
31642         
31643         var _this = this;
31644         
31645         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31646         
31647         image.on('click', this.onClick, this, file);
31648         
31649         this.fireEvent('previewrendered', this, file);
31650         
31651         return file;
31652         
31653     },
31654     
31655     onPreviewLoad : function(file, image)
31656     {
31657         if(typeof(file.target) == 'undefined' || !file.target){
31658             return;
31659         }
31660         
31661         var width = image.dom.naturalWidth || image.dom.width;
31662         var height = image.dom.naturalHeight || image.dom.height;
31663         
31664         if(!this.previewResize) {
31665             return;
31666         }
31667         
31668         if(width > height){
31669             file.target.addClass('wide');
31670             return;
31671         }
31672         
31673         file.target.addClass('tall');
31674         return;
31675         
31676     },
31677     
31678     uploadFromSource : function(file, crop)
31679     {
31680         this.xhr = new XMLHttpRequest();
31681         
31682         this.managerEl.createChild({
31683             tag : 'div',
31684             cls : 'roo-document-manager-loading',
31685             cn : [
31686                 {
31687                     tag : 'div',
31688                     tooltip : file.name,
31689                     cls : 'roo-document-manager-thumb',
31690                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31691                 }
31692             ]
31693
31694         });
31695
31696         this.xhr.open(this.method, this.url, true);
31697         
31698         var headers = {
31699             "Accept": "application/json",
31700             "Cache-Control": "no-cache",
31701             "X-Requested-With": "XMLHttpRequest"
31702         };
31703         
31704         for (var headerName in headers) {
31705             var headerValue = headers[headerName];
31706             if (headerValue) {
31707                 this.xhr.setRequestHeader(headerName, headerValue);
31708             }
31709         }
31710         
31711         var _this = this;
31712         
31713         this.xhr.onload = function()
31714         {
31715             _this.xhrOnLoad(_this.xhr);
31716         }
31717         
31718         this.xhr.onerror = function()
31719         {
31720             _this.xhrOnError(_this.xhr);
31721         }
31722         
31723         var formData = new FormData();
31724
31725         formData.append('returnHTML', 'NO');
31726         
31727         formData.append('crop', crop);
31728         
31729         if(typeof(file.filename) != 'undefined'){
31730             formData.append('filename', file.filename);
31731         }
31732         
31733         if(typeof(file.mimetype) != 'undefined'){
31734             formData.append('mimetype', file.mimetype);
31735         }
31736         
31737         Roo.log(formData);
31738         
31739         if(this.fireEvent('prepare', this, formData) != false){
31740             this.xhr.send(formData);
31741         };
31742     }
31743 });
31744
31745 /*
31746 * Licence: LGPL
31747 */
31748
31749 /**
31750  * @class Roo.bootstrap.DocumentViewer
31751  * @extends Roo.bootstrap.Component
31752  * Bootstrap DocumentViewer class
31753  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31754  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31755  * 
31756  * @constructor
31757  * Create a new DocumentViewer
31758  * @param {Object} config The config object
31759  */
31760
31761 Roo.bootstrap.DocumentViewer = function(config){
31762     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31763     
31764     this.addEvents({
31765         /**
31766          * @event initial
31767          * Fire after initEvent
31768          * @param {Roo.bootstrap.DocumentViewer} this
31769          */
31770         "initial" : true,
31771         /**
31772          * @event click
31773          * Fire after click
31774          * @param {Roo.bootstrap.DocumentViewer} this
31775          */
31776         "click" : true,
31777         /**
31778          * @event download
31779          * Fire after download button
31780          * @param {Roo.bootstrap.DocumentViewer} this
31781          */
31782         "download" : true,
31783         /**
31784          * @event trash
31785          * Fire after trash button
31786          * @param {Roo.bootstrap.DocumentViewer} this
31787          */
31788         "trash" : true
31789         
31790     });
31791 };
31792
31793 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31794     
31795     showDownload : true,
31796     
31797     showTrash : true,
31798     
31799     getAutoCreate : function()
31800     {
31801         var cfg = {
31802             tag : 'div',
31803             cls : 'roo-document-viewer',
31804             cn : [
31805                 {
31806                     tag : 'div',
31807                     cls : 'roo-document-viewer-body',
31808                     cn : [
31809                         {
31810                             tag : 'div',
31811                             cls : 'roo-document-viewer-thumb',
31812                             cn : [
31813                                 {
31814                                     tag : 'img',
31815                                     cls : 'roo-document-viewer-image'
31816                                 }
31817                             ]
31818                         }
31819                     ]
31820                 },
31821                 {
31822                     tag : 'div',
31823                     cls : 'roo-document-viewer-footer',
31824                     cn : {
31825                         tag : 'div',
31826                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31827                         cn : [
31828                             {
31829                                 tag : 'div',
31830                                 cls : 'btn-group roo-document-viewer-download',
31831                                 cn : [
31832                                     {
31833                                         tag : 'button',
31834                                         cls : 'btn btn-default',
31835                                         html : '<i class="fa fa-download"></i>'
31836                                     }
31837                                 ]
31838                             },
31839                             {
31840                                 tag : 'div',
31841                                 cls : 'btn-group roo-document-viewer-trash',
31842                                 cn : [
31843                                     {
31844                                         tag : 'button',
31845                                         cls : 'btn btn-default',
31846                                         html : '<i class="fa fa-trash"></i>'
31847                                     }
31848                                 ]
31849                             }
31850                         ]
31851                     }
31852                 }
31853             ]
31854         };
31855         
31856         return cfg;
31857     },
31858     
31859     initEvents : function()
31860     {
31861         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31862         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31863         
31864         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31865         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31866         
31867         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31868         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31869         
31870         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31871         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31872         
31873         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31874         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31875         
31876         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31877         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31878         
31879         this.bodyEl.on('click', this.onClick, this);
31880         this.downloadBtn.on('click', this.onDownload, this);
31881         this.trashBtn.on('click', this.onTrash, this);
31882         
31883         this.downloadBtn.hide();
31884         this.trashBtn.hide();
31885         
31886         if(this.showDownload){
31887             this.downloadBtn.show();
31888         }
31889         
31890         if(this.showTrash){
31891             this.trashBtn.show();
31892         }
31893         
31894         if(!this.showDownload && !this.showTrash) {
31895             this.footerEl.hide();
31896         }
31897         
31898     },
31899     
31900     initial : function()
31901     {
31902         this.fireEvent('initial', this);
31903         
31904     },
31905     
31906     onClick : function(e)
31907     {
31908         e.preventDefault();
31909         
31910         this.fireEvent('click', this);
31911     },
31912     
31913     onDownload : function(e)
31914     {
31915         e.preventDefault();
31916         
31917         this.fireEvent('download', this);
31918     },
31919     
31920     onTrash : function(e)
31921     {
31922         e.preventDefault();
31923         
31924         this.fireEvent('trash', this);
31925     }
31926     
31927 });
31928 /*
31929  * - LGPL
31930  *
31931  * nav progress bar
31932  * 
31933  */
31934
31935 /**
31936  * @class Roo.bootstrap.NavProgressBar
31937  * @extends Roo.bootstrap.Component
31938  * Bootstrap NavProgressBar class
31939  * 
31940  * @constructor
31941  * Create a new nav progress bar
31942  * @param {Object} config The config object
31943  */
31944
31945 Roo.bootstrap.NavProgressBar = function(config){
31946     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31947
31948     this.bullets = this.bullets || [];
31949    
31950 //    Roo.bootstrap.NavProgressBar.register(this);
31951      this.addEvents({
31952         /**
31953              * @event changed
31954              * Fires when the active item changes
31955              * @param {Roo.bootstrap.NavProgressBar} this
31956              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31957              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31958          */
31959         'changed': true
31960      });
31961     
31962 };
31963
31964 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31965     
31966     bullets : [],
31967     barItems : [],
31968     
31969     getAutoCreate : function()
31970     {
31971         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31972         
31973         cfg = {
31974             tag : 'div',
31975             cls : 'roo-navigation-bar-group',
31976             cn : [
31977                 {
31978                     tag : 'div',
31979                     cls : 'roo-navigation-top-bar'
31980                 },
31981                 {
31982                     tag : 'div',
31983                     cls : 'roo-navigation-bullets-bar',
31984                     cn : [
31985                         {
31986                             tag : 'ul',
31987                             cls : 'roo-navigation-bar'
31988                         }
31989                     ]
31990                 },
31991                 
31992                 {
31993                     tag : 'div',
31994                     cls : 'roo-navigation-bottom-bar'
31995                 }
31996             ]
31997             
31998         };
31999         
32000         return cfg;
32001         
32002     },
32003     
32004     initEvents: function() 
32005     {
32006         
32007     },
32008     
32009     onRender : function(ct, position) 
32010     {
32011         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32012         
32013         if(this.bullets.length){
32014             Roo.each(this.bullets, function(b){
32015                this.addItem(b);
32016             }, this);
32017         }
32018         
32019         this.format();
32020         
32021     },
32022     
32023     addItem : function(cfg)
32024     {
32025         var item = new Roo.bootstrap.NavProgressItem(cfg);
32026         
32027         item.parentId = this.id;
32028         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32029         
32030         if(cfg.html){
32031             var top = new Roo.bootstrap.Element({
32032                 tag : 'div',
32033                 cls : 'roo-navigation-bar-text'
32034             });
32035             
32036             var bottom = new Roo.bootstrap.Element({
32037                 tag : 'div',
32038                 cls : 'roo-navigation-bar-text'
32039             });
32040             
32041             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32042             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32043             
32044             var topText = new Roo.bootstrap.Element({
32045                 tag : 'span',
32046                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32047             });
32048             
32049             var bottomText = new Roo.bootstrap.Element({
32050                 tag : 'span',
32051                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32052             });
32053             
32054             topText.onRender(top.el, null);
32055             bottomText.onRender(bottom.el, null);
32056             
32057             item.topEl = top;
32058             item.bottomEl = bottom;
32059         }
32060         
32061         this.barItems.push(item);
32062         
32063         return item;
32064     },
32065     
32066     getActive : function()
32067     {
32068         var active = false;
32069         
32070         Roo.each(this.barItems, function(v){
32071             
32072             if (!v.isActive()) {
32073                 return;
32074             }
32075             
32076             active = v;
32077             return false;
32078             
32079         });
32080         
32081         return active;
32082     },
32083     
32084     setActiveItem : function(item)
32085     {
32086         var prev = false;
32087         
32088         Roo.each(this.barItems, function(v){
32089             if (v.rid == item.rid) {
32090                 return ;
32091             }
32092             
32093             if (v.isActive()) {
32094                 v.setActive(false);
32095                 prev = v;
32096             }
32097         });
32098
32099         item.setActive(true);
32100         
32101         this.fireEvent('changed', this, item, prev);
32102     },
32103     
32104     getBarItem: function(rid)
32105     {
32106         var ret = false;
32107         
32108         Roo.each(this.barItems, function(e) {
32109             if (e.rid != rid) {
32110                 return;
32111             }
32112             
32113             ret =  e;
32114             return false;
32115         });
32116         
32117         return ret;
32118     },
32119     
32120     indexOfItem : function(item)
32121     {
32122         var index = false;
32123         
32124         Roo.each(this.barItems, function(v, i){
32125             
32126             if (v.rid != item.rid) {
32127                 return;
32128             }
32129             
32130             index = i;
32131             return false
32132         });
32133         
32134         return index;
32135     },
32136     
32137     setActiveNext : function()
32138     {
32139         var i = this.indexOfItem(this.getActive());
32140         
32141         if (i > this.barItems.length) {
32142             return;
32143         }
32144         
32145         this.setActiveItem(this.barItems[i+1]);
32146     },
32147     
32148     setActivePrev : function()
32149     {
32150         var i = this.indexOfItem(this.getActive());
32151         
32152         if (i  < 1) {
32153             return;
32154         }
32155         
32156         this.setActiveItem(this.barItems[i-1]);
32157     },
32158     
32159     format : function()
32160     {
32161         if(!this.barItems.length){
32162             return;
32163         }
32164      
32165         var width = 100 / this.barItems.length;
32166         
32167         Roo.each(this.barItems, function(i){
32168             i.el.setStyle('width', width + '%');
32169             i.topEl.el.setStyle('width', width + '%');
32170             i.bottomEl.el.setStyle('width', width + '%');
32171         }, this);
32172         
32173     }
32174     
32175 });
32176 /*
32177  * - LGPL
32178  *
32179  * Nav Progress Item
32180  * 
32181  */
32182
32183 /**
32184  * @class Roo.bootstrap.NavProgressItem
32185  * @extends Roo.bootstrap.Component
32186  * Bootstrap NavProgressItem class
32187  * @cfg {String} rid the reference id
32188  * @cfg {Boolean} active (true|false) Is item active default false
32189  * @cfg {Boolean} disabled (true|false) Is item active default false
32190  * @cfg {String} html
32191  * @cfg {String} position (top|bottom) text position default bottom
32192  * @cfg {String} icon show icon instead of number
32193  * 
32194  * @constructor
32195  * Create a new NavProgressItem
32196  * @param {Object} config The config object
32197  */
32198 Roo.bootstrap.NavProgressItem = function(config){
32199     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32200     this.addEvents({
32201         // raw events
32202         /**
32203          * @event click
32204          * The raw click event for the entire grid.
32205          * @param {Roo.bootstrap.NavProgressItem} this
32206          * @param {Roo.EventObject} e
32207          */
32208         "click" : true
32209     });
32210    
32211 };
32212
32213 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32214     
32215     rid : '',
32216     active : false,
32217     disabled : false,
32218     html : '',
32219     position : 'bottom',
32220     icon : false,
32221     
32222     getAutoCreate : function()
32223     {
32224         var iconCls = 'roo-navigation-bar-item-icon';
32225         
32226         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32227         
32228         var cfg = {
32229             tag: 'li',
32230             cls: 'roo-navigation-bar-item',
32231             cn : [
32232                 {
32233                     tag : 'i',
32234                     cls : iconCls
32235                 }
32236             ]
32237         };
32238         
32239         if(this.active){
32240             cfg.cls += ' active';
32241         }
32242         if(this.disabled){
32243             cfg.cls += ' disabled';
32244         }
32245         
32246         return cfg;
32247     },
32248     
32249     disable : function()
32250     {
32251         this.setDisabled(true);
32252     },
32253     
32254     enable : function()
32255     {
32256         this.setDisabled(false);
32257     },
32258     
32259     initEvents: function() 
32260     {
32261         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32262         
32263         this.iconEl.on('click', this.onClick, this);
32264     },
32265     
32266     onClick : function(e)
32267     {
32268         e.preventDefault();
32269         
32270         if(this.disabled){
32271             return;
32272         }
32273         
32274         if(this.fireEvent('click', this, e) === false){
32275             return;
32276         };
32277         
32278         this.parent().setActiveItem(this);
32279     },
32280     
32281     isActive: function () 
32282     {
32283         return this.active;
32284     },
32285     
32286     setActive : function(state)
32287     {
32288         if(this.active == state){
32289             return;
32290         }
32291         
32292         this.active = state;
32293         
32294         if (state) {
32295             this.el.addClass('active');
32296             return;
32297         }
32298         
32299         this.el.removeClass('active');
32300         
32301         return;
32302     },
32303     
32304     setDisabled : function(state)
32305     {
32306         if(this.disabled == state){
32307             return;
32308         }
32309         
32310         this.disabled = state;
32311         
32312         if (state) {
32313             this.el.addClass('disabled');
32314             return;
32315         }
32316         
32317         this.el.removeClass('disabled');
32318     },
32319     
32320     tooltipEl : function()
32321     {
32322         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32323     }
32324 });
32325  
32326
32327  /*
32328  * - LGPL
32329  *
32330  * FieldLabel
32331  * 
32332  */
32333
32334 /**
32335  * @class Roo.bootstrap.FieldLabel
32336  * @extends Roo.bootstrap.Component
32337  * Bootstrap FieldLabel class
32338  * @cfg {String} html contents of the element
32339  * @cfg {String} tag tag of the element default label
32340  * @cfg {String} cls class of the element
32341  * @cfg {String} target label target 
32342  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32343  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32344  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32345  * @cfg {String} iconTooltip default "This field is required"
32346  * @cfg {String} indicatorpos (left|right) default left
32347  * 
32348  * @constructor
32349  * Create a new FieldLabel
32350  * @param {Object} config The config object
32351  */
32352
32353 Roo.bootstrap.FieldLabel = function(config){
32354     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32355     
32356     this.addEvents({
32357             /**
32358              * @event invalid
32359              * Fires after the field has been marked as invalid.
32360              * @param {Roo.form.FieldLabel} this
32361              * @param {String} msg The validation message
32362              */
32363             invalid : true,
32364             /**
32365              * @event valid
32366              * Fires after the field has been validated with no errors.
32367              * @param {Roo.form.FieldLabel} this
32368              */
32369             valid : true
32370         });
32371 };
32372
32373 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32374     
32375     tag: 'label',
32376     cls: '',
32377     html: '',
32378     target: '',
32379     allowBlank : true,
32380     invalidClass : 'has-warning',
32381     validClass : 'has-success',
32382     iconTooltip : 'This field is required',
32383     indicatorpos : 'left',
32384     
32385     getAutoCreate : function(){
32386         
32387         var cls = "";
32388         if (!this.allowBlank) {
32389             cls  = "visible";
32390         }
32391         
32392         var cfg = {
32393             tag : this.tag,
32394             cls : 'roo-bootstrap-field-label ' + this.cls,
32395             for : this.target,
32396             cn : [
32397                 {
32398                     tag : 'i',
32399                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32400                     tooltip : this.iconTooltip
32401                 },
32402                 {
32403                     tag : 'span',
32404                     html : this.html
32405                 }
32406             ] 
32407         };
32408         
32409         if(this.indicatorpos == 'right'){
32410             var cfg = {
32411                 tag : this.tag,
32412                 cls : 'roo-bootstrap-field-label ' + this.cls,
32413                 for : this.target,
32414                 cn : [
32415                     {
32416                         tag : 'span',
32417                         html : this.html
32418                     },
32419                     {
32420                         tag : 'i',
32421                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32422                         tooltip : this.iconTooltip
32423                     }
32424                 ] 
32425             };
32426         }
32427         
32428         return cfg;
32429     },
32430     
32431     initEvents: function() 
32432     {
32433         Roo.bootstrap.Element.superclass.initEvents.call(this);
32434         
32435         this.indicator = this.indicatorEl();
32436         
32437         if(this.indicator){
32438             this.indicator.removeClass('visible');
32439             this.indicator.addClass('invisible');
32440         }
32441         
32442         Roo.bootstrap.FieldLabel.register(this);
32443     },
32444     
32445     indicatorEl : function()
32446     {
32447         var indicator = this.el.select('i.roo-required-indicator',true).first();
32448         
32449         if(!indicator){
32450             return false;
32451         }
32452         
32453         return indicator;
32454         
32455     },
32456     
32457     /**
32458      * Mark this field as valid
32459      */
32460     markValid : function()
32461     {
32462         if(this.indicator){
32463             this.indicator.removeClass('visible');
32464             this.indicator.addClass('invisible');
32465         }
32466         if (Roo.bootstrap.version == 3) {
32467             this.el.removeClass(this.invalidClass);
32468             this.el.addClass(this.validClass);
32469         } else {
32470             this.el.removeClass('is-invalid');
32471             this.el.addClass('is-valid');
32472         }
32473         
32474         
32475         this.fireEvent('valid', this);
32476     },
32477     
32478     /**
32479      * Mark this field as invalid
32480      * @param {String} msg The validation message
32481      */
32482     markInvalid : function(msg)
32483     {
32484         if(this.indicator){
32485             this.indicator.removeClass('invisible');
32486             this.indicator.addClass('visible');
32487         }
32488           if (Roo.bootstrap.version == 3) {
32489             this.el.removeClass(this.validClass);
32490             this.el.addClass(this.invalidClass);
32491         } else {
32492             this.el.removeClass('is-valid');
32493             this.el.addClass('is-invalid');
32494         }
32495         
32496         
32497         this.fireEvent('invalid', this, msg);
32498     }
32499     
32500    
32501 });
32502
32503 Roo.apply(Roo.bootstrap.FieldLabel, {
32504     
32505     groups: {},
32506     
32507      /**
32508     * register a FieldLabel Group
32509     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32510     */
32511     register : function(label)
32512     {
32513         if(this.groups.hasOwnProperty(label.target)){
32514             return;
32515         }
32516      
32517         this.groups[label.target] = label;
32518         
32519     },
32520     /**
32521     * fetch a FieldLabel Group based on the target
32522     * @param {string} target
32523     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32524     */
32525     get: function(target) {
32526         if (typeof(this.groups[target]) == 'undefined') {
32527             return false;
32528         }
32529         
32530         return this.groups[target] ;
32531     }
32532 });
32533
32534  
32535
32536  /*
32537  * - LGPL
32538  *
32539  * page DateSplitField.
32540  * 
32541  */
32542
32543
32544 /**
32545  * @class Roo.bootstrap.DateSplitField
32546  * @extends Roo.bootstrap.Component
32547  * Bootstrap DateSplitField class
32548  * @cfg {string} fieldLabel - the label associated
32549  * @cfg {Number} labelWidth set the width of label (0-12)
32550  * @cfg {String} labelAlign (top|left)
32551  * @cfg {Boolean} dayAllowBlank (true|false) default false
32552  * @cfg {Boolean} monthAllowBlank (true|false) default false
32553  * @cfg {Boolean} yearAllowBlank (true|false) default false
32554  * @cfg {string} dayPlaceholder 
32555  * @cfg {string} monthPlaceholder
32556  * @cfg {string} yearPlaceholder
32557  * @cfg {string} dayFormat default 'd'
32558  * @cfg {string} monthFormat default 'm'
32559  * @cfg {string} yearFormat default 'Y'
32560  * @cfg {Number} labellg set the width of label (1-12)
32561  * @cfg {Number} labelmd set the width of label (1-12)
32562  * @cfg {Number} labelsm set the width of label (1-12)
32563  * @cfg {Number} labelxs set the width of label (1-12)
32564
32565  *     
32566  * @constructor
32567  * Create a new DateSplitField
32568  * @param {Object} config The config object
32569  */
32570
32571 Roo.bootstrap.DateSplitField = function(config){
32572     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32573     
32574     this.addEvents({
32575         // raw events
32576          /**
32577          * @event years
32578          * getting the data of years
32579          * @param {Roo.bootstrap.DateSplitField} this
32580          * @param {Object} years
32581          */
32582         "years" : true,
32583         /**
32584          * @event days
32585          * getting the data of days
32586          * @param {Roo.bootstrap.DateSplitField} this
32587          * @param {Object} days
32588          */
32589         "days" : true,
32590         /**
32591          * @event invalid
32592          * Fires after the field has been marked as invalid.
32593          * @param {Roo.form.Field} this
32594          * @param {String} msg The validation message
32595          */
32596         invalid : true,
32597        /**
32598          * @event valid
32599          * Fires after the field has been validated with no errors.
32600          * @param {Roo.form.Field} this
32601          */
32602         valid : true
32603     });
32604 };
32605
32606 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32607     
32608     fieldLabel : '',
32609     labelAlign : 'top',
32610     labelWidth : 3,
32611     dayAllowBlank : false,
32612     monthAllowBlank : false,
32613     yearAllowBlank : false,
32614     dayPlaceholder : '',
32615     monthPlaceholder : '',
32616     yearPlaceholder : '',
32617     dayFormat : 'd',
32618     monthFormat : 'm',
32619     yearFormat : 'Y',
32620     isFormField : true,
32621     labellg : 0,
32622     labelmd : 0,
32623     labelsm : 0,
32624     labelxs : 0,
32625     
32626     getAutoCreate : function()
32627     {
32628         var cfg = {
32629             tag : 'div',
32630             cls : 'row roo-date-split-field-group',
32631             cn : [
32632                 {
32633                     tag : 'input',
32634                     type : 'hidden',
32635                     cls : 'form-hidden-field roo-date-split-field-group-value',
32636                     name : this.name
32637                 }
32638             ]
32639         };
32640         
32641         var labelCls = 'col-md-12';
32642         var contentCls = 'col-md-4';
32643         
32644         if(this.fieldLabel){
32645             
32646             var label = {
32647                 tag : 'div',
32648                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32649                 cn : [
32650                     {
32651                         tag : 'label',
32652                         html : this.fieldLabel
32653                     }
32654                 ]
32655             };
32656             
32657             if(this.labelAlign == 'left'){
32658             
32659                 if(this.labelWidth > 12){
32660                     label.style = "width: " + this.labelWidth + 'px';
32661                 }
32662
32663                 if(this.labelWidth < 13 && this.labelmd == 0){
32664                     this.labelmd = this.labelWidth;
32665                 }
32666
32667                 if(this.labellg > 0){
32668                     labelCls = ' col-lg-' + this.labellg;
32669                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32670                 }
32671
32672                 if(this.labelmd > 0){
32673                     labelCls = ' col-md-' + this.labelmd;
32674                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32675                 }
32676
32677                 if(this.labelsm > 0){
32678                     labelCls = ' col-sm-' + this.labelsm;
32679                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32680                 }
32681
32682                 if(this.labelxs > 0){
32683                     labelCls = ' col-xs-' + this.labelxs;
32684                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32685                 }
32686             }
32687             
32688             label.cls += ' ' + labelCls;
32689             
32690             cfg.cn.push(label);
32691         }
32692         
32693         Roo.each(['day', 'month', 'year'], function(t){
32694             cfg.cn.push({
32695                 tag : 'div',
32696                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32697             });
32698         }, this);
32699         
32700         return cfg;
32701     },
32702     
32703     inputEl: function ()
32704     {
32705         return this.el.select('.roo-date-split-field-group-value', true).first();
32706     },
32707     
32708     onRender : function(ct, position) 
32709     {
32710         var _this = this;
32711         
32712         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32713         
32714         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32715         
32716         this.dayField = new Roo.bootstrap.ComboBox({
32717             allowBlank : this.dayAllowBlank,
32718             alwaysQuery : true,
32719             displayField : 'value',
32720             editable : false,
32721             fieldLabel : '',
32722             forceSelection : true,
32723             mode : 'local',
32724             placeholder : this.dayPlaceholder,
32725             selectOnFocus : true,
32726             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32727             triggerAction : 'all',
32728             typeAhead : true,
32729             valueField : 'value',
32730             store : new Roo.data.SimpleStore({
32731                 data : (function() {    
32732                     var days = [];
32733                     _this.fireEvent('days', _this, days);
32734                     return days;
32735                 })(),
32736                 fields : [ 'value' ]
32737             }),
32738             listeners : {
32739                 select : function (_self, record, index)
32740                 {
32741                     _this.setValue(_this.getValue());
32742                 }
32743             }
32744         });
32745
32746         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32747         
32748         this.monthField = new Roo.bootstrap.MonthField({
32749             after : '<i class=\"fa fa-calendar\"></i>',
32750             allowBlank : this.monthAllowBlank,
32751             placeholder : this.monthPlaceholder,
32752             readOnly : true,
32753             listeners : {
32754                 render : function (_self)
32755                 {
32756                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32757                         e.preventDefault();
32758                         _self.focus();
32759                     });
32760                 },
32761                 select : function (_self, oldvalue, newvalue)
32762                 {
32763                     _this.setValue(_this.getValue());
32764                 }
32765             }
32766         });
32767         
32768         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32769         
32770         this.yearField = new Roo.bootstrap.ComboBox({
32771             allowBlank : this.yearAllowBlank,
32772             alwaysQuery : true,
32773             displayField : 'value',
32774             editable : false,
32775             fieldLabel : '',
32776             forceSelection : true,
32777             mode : 'local',
32778             placeholder : this.yearPlaceholder,
32779             selectOnFocus : true,
32780             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32781             triggerAction : 'all',
32782             typeAhead : true,
32783             valueField : 'value',
32784             store : new Roo.data.SimpleStore({
32785                 data : (function() {
32786                     var years = [];
32787                     _this.fireEvent('years', _this, years);
32788                     return years;
32789                 })(),
32790                 fields : [ 'value' ]
32791             }),
32792             listeners : {
32793                 select : function (_self, record, index)
32794                 {
32795                     _this.setValue(_this.getValue());
32796                 }
32797             }
32798         });
32799
32800         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32801     },
32802     
32803     setValue : function(v, format)
32804     {
32805         this.inputEl.dom.value = v;
32806         
32807         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32808         
32809         var d = Date.parseDate(v, f);
32810         
32811         if(!d){
32812             this.validate();
32813             return;
32814         }
32815         
32816         this.setDay(d.format(this.dayFormat));
32817         this.setMonth(d.format(this.monthFormat));
32818         this.setYear(d.format(this.yearFormat));
32819         
32820         this.validate();
32821         
32822         return;
32823     },
32824     
32825     setDay : function(v)
32826     {
32827         this.dayField.setValue(v);
32828         this.inputEl.dom.value = this.getValue();
32829         this.validate();
32830         return;
32831     },
32832     
32833     setMonth : function(v)
32834     {
32835         this.monthField.setValue(v, true);
32836         this.inputEl.dom.value = this.getValue();
32837         this.validate();
32838         return;
32839     },
32840     
32841     setYear : function(v)
32842     {
32843         this.yearField.setValue(v);
32844         this.inputEl.dom.value = this.getValue();
32845         this.validate();
32846         return;
32847     },
32848     
32849     getDay : function()
32850     {
32851         return this.dayField.getValue();
32852     },
32853     
32854     getMonth : function()
32855     {
32856         return this.monthField.getValue();
32857     },
32858     
32859     getYear : function()
32860     {
32861         return this.yearField.getValue();
32862     },
32863     
32864     getValue : function()
32865     {
32866         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32867         
32868         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32869         
32870         return date;
32871     },
32872     
32873     reset : function()
32874     {
32875         this.setDay('');
32876         this.setMonth('');
32877         this.setYear('');
32878         this.inputEl.dom.value = '';
32879         this.validate();
32880         return;
32881     },
32882     
32883     validate : function()
32884     {
32885         var d = this.dayField.validate();
32886         var m = this.monthField.validate();
32887         var y = this.yearField.validate();
32888         
32889         var valid = true;
32890         
32891         if(
32892                 (!this.dayAllowBlank && !d) ||
32893                 (!this.monthAllowBlank && !m) ||
32894                 (!this.yearAllowBlank && !y)
32895         ){
32896             valid = false;
32897         }
32898         
32899         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32900             return valid;
32901         }
32902         
32903         if(valid){
32904             this.markValid();
32905             return valid;
32906         }
32907         
32908         this.markInvalid();
32909         
32910         return valid;
32911     },
32912     
32913     markValid : function()
32914     {
32915         
32916         var label = this.el.select('label', true).first();
32917         var icon = this.el.select('i.fa-star', true).first();
32918
32919         if(label && icon){
32920             icon.remove();
32921         }
32922         
32923         this.fireEvent('valid', this);
32924     },
32925     
32926      /**
32927      * Mark this field as invalid
32928      * @param {String} msg The validation message
32929      */
32930     markInvalid : function(msg)
32931     {
32932         
32933         var label = this.el.select('label', true).first();
32934         var icon = this.el.select('i.fa-star', true).first();
32935
32936         if(label && !icon){
32937             this.el.select('.roo-date-split-field-label', true).createChild({
32938                 tag : 'i',
32939                 cls : 'text-danger fa fa-lg fa-star',
32940                 tooltip : 'This field is required',
32941                 style : 'margin-right:5px;'
32942             }, label, true);
32943         }
32944         
32945         this.fireEvent('invalid', this, msg);
32946     },
32947     
32948     clearInvalid : function()
32949     {
32950         var label = this.el.select('label', true).first();
32951         var icon = this.el.select('i.fa-star', true).first();
32952
32953         if(label && icon){
32954             icon.remove();
32955         }
32956         
32957         this.fireEvent('valid', this);
32958     },
32959     
32960     getName: function()
32961     {
32962         return this.name;
32963     }
32964     
32965 });
32966
32967  /**
32968  *
32969  * This is based on 
32970  * http://masonry.desandro.com
32971  *
32972  * The idea is to render all the bricks based on vertical width...
32973  *
32974  * The original code extends 'outlayer' - we might need to use that....
32975  * 
32976  */
32977
32978
32979 /**
32980  * @class Roo.bootstrap.LayoutMasonry
32981  * @extends Roo.bootstrap.Component
32982  * Bootstrap Layout Masonry class
32983  * 
32984  * @constructor
32985  * Create a new Element
32986  * @param {Object} config The config object
32987  */
32988
32989 Roo.bootstrap.LayoutMasonry = function(config){
32990     
32991     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32992     
32993     this.bricks = [];
32994     
32995     Roo.bootstrap.LayoutMasonry.register(this);
32996     
32997     this.addEvents({
32998         // raw events
32999         /**
33000          * @event layout
33001          * Fire after layout the items
33002          * @param {Roo.bootstrap.LayoutMasonry} this
33003          * @param {Roo.EventObject} e
33004          */
33005         "layout" : true
33006     });
33007     
33008 };
33009
33010 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33011     
33012     /**
33013      * @cfg {Boolean} isLayoutInstant = no animation?
33014      */   
33015     isLayoutInstant : false, // needed?
33016    
33017     /**
33018      * @cfg {Number} boxWidth  width of the columns
33019      */   
33020     boxWidth : 450,
33021     
33022       /**
33023      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33024      */   
33025     boxHeight : 0,
33026     
33027     /**
33028      * @cfg {Number} padWidth padding below box..
33029      */   
33030     padWidth : 10, 
33031     
33032     /**
33033      * @cfg {Number} gutter gutter width..
33034      */   
33035     gutter : 10,
33036     
33037      /**
33038      * @cfg {Number} maxCols maximum number of columns
33039      */   
33040     
33041     maxCols: 0,
33042     
33043     /**
33044      * @cfg {Boolean} isAutoInitial defalut true
33045      */   
33046     isAutoInitial : true, 
33047     
33048     containerWidth: 0,
33049     
33050     /**
33051      * @cfg {Boolean} isHorizontal defalut false
33052      */   
33053     isHorizontal : false, 
33054
33055     currentSize : null,
33056     
33057     tag: 'div',
33058     
33059     cls: '',
33060     
33061     bricks: null, //CompositeElement
33062     
33063     cols : 1,
33064     
33065     _isLayoutInited : false,
33066     
33067 //    isAlternative : false, // only use for vertical layout...
33068     
33069     /**
33070      * @cfg {Number} alternativePadWidth padding below box..
33071      */   
33072     alternativePadWidth : 50,
33073     
33074     selectedBrick : [],
33075     
33076     getAutoCreate : function(){
33077         
33078         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33079         
33080         var cfg = {
33081             tag: this.tag,
33082             cls: 'blog-masonary-wrapper ' + this.cls,
33083             cn : {
33084                 cls : 'mas-boxes masonary'
33085             }
33086         };
33087         
33088         return cfg;
33089     },
33090     
33091     getChildContainer: function( )
33092     {
33093         if (this.boxesEl) {
33094             return this.boxesEl;
33095         }
33096         
33097         this.boxesEl = this.el.select('.mas-boxes').first();
33098         
33099         return this.boxesEl;
33100     },
33101     
33102     
33103     initEvents : function()
33104     {
33105         var _this = this;
33106         
33107         if(this.isAutoInitial){
33108             Roo.log('hook children rendered');
33109             this.on('childrenrendered', function() {
33110                 Roo.log('children rendered');
33111                 _this.initial();
33112             } ,this);
33113         }
33114     },
33115     
33116     initial : function()
33117     {
33118         this.selectedBrick = [];
33119         
33120         this.currentSize = this.el.getBox(true);
33121         
33122         Roo.EventManager.onWindowResize(this.resize, this); 
33123
33124         if(!this.isAutoInitial){
33125             this.layout();
33126             return;
33127         }
33128         
33129         this.layout();
33130         
33131         return;
33132         //this.layout.defer(500,this);
33133         
33134     },
33135     
33136     resize : function()
33137     {
33138         var cs = this.el.getBox(true);
33139         
33140         if (
33141                 this.currentSize.width == cs.width && 
33142                 this.currentSize.x == cs.x && 
33143                 this.currentSize.height == cs.height && 
33144                 this.currentSize.y == cs.y 
33145         ) {
33146             Roo.log("no change in with or X or Y");
33147             return;
33148         }
33149         
33150         this.currentSize = cs;
33151         
33152         this.layout();
33153         
33154     },
33155     
33156     layout : function()
33157     {   
33158         this._resetLayout();
33159         
33160         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33161         
33162         this.layoutItems( isInstant );
33163       
33164         this._isLayoutInited = true;
33165         
33166         this.fireEvent('layout', this);
33167         
33168     },
33169     
33170     _resetLayout : function()
33171     {
33172         if(this.isHorizontal){
33173             this.horizontalMeasureColumns();
33174             return;
33175         }
33176         
33177         this.verticalMeasureColumns();
33178         
33179     },
33180     
33181     verticalMeasureColumns : function()
33182     {
33183         this.getContainerWidth();
33184         
33185 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33186 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33187 //            return;
33188 //        }
33189         
33190         var boxWidth = this.boxWidth + this.padWidth;
33191         
33192         if(this.containerWidth < this.boxWidth){
33193             boxWidth = this.containerWidth
33194         }
33195         
33196         var containerWidth = this.containerWidth;
33197         
33198         var cols = Math.floor(containerWidth / boxWidth);
33199         
33200         this.cols = Math.max( cols, 1 );
33201         
33202         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33203         
33204         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33205         
33206         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33207         
33208         this.colWidth = boxWidth + avail - this.padWidth;
33209         
33210         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33211         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33212     },
33213     
33214     horizontalMeasureColumns : function()
33215     {
33216         this.getContainerWidth();
33217         
33218         var boxWidth = this.boxWidth;
33219         
33220         if(this.containerWidth < boxWidth){
33221             boxWidth = this.containerWidth;
33222         }
33223         
33224         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33225         
33226         this.el.setHeight(boxWidth);
33227         
33228     },
33229     
33230     getContainerWidth : function()
33231     {
33232         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33233     },
33234     
33235     layoutItems : function( isInstant )
33236     {
33237         Roo.log(this.bricks);
33238         
33239         var items = Roo.apply([], this.bricks);
33240         
33241         if(this.isHorizontal){
33242             this._horizontalLayoutItems( items , isInstant );
33243             return;
33244         }
33245         
33246 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33247 //            this._verticalAlternativeLayoutItems( items , isInstant );
33248 //            return;
33249 //        }
33250         
33251         this._verticalLayoutItems( items , isInstant );
33252         
33253     },
33254     
33255     _verticalLayoutItems : function ( items , isInstant)
33256     {
33257         if ( !items || !items.length ) {
33258             return;
33259         }
33260         
33261         var standard = [
33262             ['xs', 'xs', 'xs', 'tall'],
33263             ['xs', 'xs', 'tall'],
33264             ['xs', 'xs', 'sm'],
33265             ['xs', 'xs', 'xs'],
33266             ['xs', 'tall'],
33267             ['xs', 'sm'],
33268             ['xs', 'xs'],
33269             ['xs'],
33270             
33271             ['sm', 'xs', 'xs'],
33272             ['sm', 'xs'],
33273             ['sm'],
33274             
33275             ['tall', 'xs', 'xs', 'xs'],
33276             ['tall', 'xs', 'xs'],
33277             ['tall', 'xs'],
33278             ['tall']
33279             
33280         ];
33281         
33282         var queue = [];
33283         
33284         var boxes = [];
33285         
33286         var box = [];
33287         
33288         Roo.each(items, function(item, k){
33289             
33290             switch (item.size) {
33291                 // these layouts take up a full box,
33292                 case 'md' :
33293                 case 'md-left' :
33294                 case 'md-right' :
33295                 case 'wide' :
33296                     
33297                     if(box.length){
33298                         boxes.push(box);
33299                         box = [];
33300                     }
33301                     
33302                     boxes.push([item]);
33303                     
33304                     break;
33305                     
33306                 case 'xs' :
33307                 case 'sm' :
33308                 case 'tall' :
33309                     
33310                     box.push(item);
33311                     
33312                     break;
33313                 default :
33314                     break;
33315                     
33316             }
33317             
33318         }, this);
33319         
33320         if(box.length){
33321             boxes.push(box);
33322             box = [];
33323         }
33324         
33325         var filterPattern = function(box, length)
33326         {
33327             if(!box.length){
33328                 return;
33329             }
33330             
33331             var match = false;
33332             
33333             var pattern = box.slice(0, length);
33334             
33335             var format = [];
33336             
33337             Roo.each(pattern, function(i){
33338                 format.push(i.size);
33339             }, this);
33340             
33341             Roo.each(standard, function(s){
33342                 
33343                 if(String(s) != String(format)){
33344                     return;
33345                 }
33346                 
33347                 match = true;
33348                 return false;
33349                 
33350             }, this);
33351             
33352             if(!match && length == 1){
33353                 return;
33354             }
33355             
33356             if(!match){
33357                 filterPattern(box, length - 1);
33358                 return;
33359             }
33360                 
33361             queue.push(pattern);
33362
33363             box = box.slice(length, box.length);
33364
33365             filterPattern(box, 4);
33366
33367             return;
33368             
33369         }
33370         
33371         Roo.each(boxes, function(box, k){
33372             
33373             if(!box.length){
33374                 return;
33375             }
33376             
33377             if(box.length == 1){
33378                 queue.push(box);
33379                 return;
33380             }
33381             
33382             filterPattern(box, 4);
33383             
33384         }, this);
33385         
33386         this._processVerticalLayoutQueue( queue, isInstant );
33387         
33388     },
33389     
33390 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33391 //    {
33392 //        if ( !items || !items.length ) {
33393 //            return;
33394 //        }
33395 //
33396 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33397 //        
33398 //    },
33399     
33400     _horizontalLayoutItems : function ( items , isInstant)
33401     {
33402         if ( !items || !items.length || items.length < 3) {
33403             return;
33404         }
33405         
33406         items.reverse();
33407         
33408         var eItems = items.slice(0, 3);
33409         
33410         items = items.slice(3, items.length);
33411         
33412         var standard = [
33413             ['xs', 'xs', 'xs', 'wide'],
33414             ['xs', 'xs', 'wide'],
33415             ['xs', 'xs', 'sm'],
33416             ['xs', 'xs', 'xs'],
33417             ['xs', 'wide'],
33418             ['xs', 'sm'],
33419             ['xs', 'xs'],
33420             ['xs'],
33421             
33422             ['sm', 'xs', 'xs'],
33423             ['sm', 'xs'],
33424             ['sm'],
33425             
33426             ['wide', 'xs', 'xs', 'xs'],
33427             ['wide', 'xs', 'xs'],
33428             ['wide', 'xs'],
33429             ['wide'],
33430             
33431             ['wide-thin']
33432         ];
33433         
33434         var queue = [];
33435         
33436         var boxes = [];
33437         
33438         var box = [];
33439         
33440         Roo.each(items, function(item, k){
33441             
33442             switch (item.size) {
33443                 case 'md' :
33444                 case 'md-left' :
33445                 case 'md-right' :
33446                 case 'tall' :
33447                     
33448                     if(box.length){
33449                         boxes.push(box);
33450                         box = [];
33451                     }
33452                     
33453                     boxes.push([item]);
33454                     
33455                     break;
33456                     
33457                 case 'xs' :
33458                 case 'sm' :
33459                 case 'wide' :
33460                 case 'wide-thin' :
33461                     
33462                     box.push(item);
33463                     
33464                     break;
33465                 default :
33466                     break;
33467                     
33468             }
33469             
33470         }, this);
33471         
33472         if(box.length){
33473             boxes.push(box);
33474             box = [];
33475         }
33476         
33477         var filterPattern = function(box, length)
33478         {
33479             if(!box.length){
33480                 return;
33481             }
33482             
33483             var match = false;
33484             
33485             var pattern = box.slice(0, length);
33486             
33487             var format = [];
33488             
33489             Roo.each(pattern, function(i){
33490                 format.push(i.size);
33491             }, this);
33492             
33493             Roo.each(standard, function(s){
33494                 
33495                 if(String(s) != String(format)){
33496                     return;
33497                 }
33498                 
33499                 match = true;
33500                 return false;
33501                 
33502             }, this);
33503             
33504             if(!match && length == 1){
33505                 return;
33506             }
33507             
33508             if(!match){
33509                 filterPattern(box, length - 1);
33510                 return;
33511             }
33512                 
33513             queue.push(pattern);
33514
33515             box = box.slice(length, box.length);
33516
33517             filterPattern(box, 4);
33518
33519             return;
33520             
33521         }
33522         
33523         Roo.each(boxes, function(box, k){
33524             
33525             if(!box.length){
33526                 return;
33527             }
33528             
33529             if(box.length == 1){
33530                 queue.push(box);
33531                 return;
33532             }
33533             
33534             filterPattern(box, 4);
33535             
33536         }, this);
33537         
33538         
33539         var prune = [];
33540         
33541         var pos = this.el.getBox(true);
33542         
33543         var minX = pos.x;
33544         
33545         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33546         
33547         var hit_end = false;
33548         
33549         Roo.each(queue, function(box){
33550             
33551             if(hit_end){
33552                 
33553                 Roo.each(box, function(b){
33554                 
33555                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33556                     b.el.hide();
33557
33558                 }, this);
33559
33560                 return;
33561             }
33562             
33563             var mx = 0;
33564             
33565             Roo.each(box, function(b){
33566                 
33567                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33568                 b.el.show();
33569
33570                 mx = Math.max(mx, b.x);
33571                 
33572             }, this);
33573             
33574             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33575             
33576             if(maxX < minX){
33577                 
33578                 Roo.each(box, function(b){
33579                 
33580                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33581                     b.el.hide();
33582                     
33583                 }, this);
33584                 
33585                 hit_end = true;
33586                 
33587                 return;
33588             }
33589             
33590             prune.push(box);
33591             
33592         }, this);
33593         
33594         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33595     },
33596     
33597     /** Sets position of item in DOM
33598     * @param {Element} item
33599     * @param {Number} x - horizontal position
33600     * @param {Number} y - vertical position
33601     * @param {Boolean} isInstant - disables transitions
33602     */
33603     _processVerticalLayoutQueue : function( queue, isInstant )
33604     {
33605         var pos = this.el.getBox(true);
33606         var x = pos.x;
33607         var y = pos.y;
33608         var maxY = [];
33609         
33610         for (var i = 0; i < this.cols; i++){
33611             maxY[i] = pos.y;
33612         }
33613         
33614         Roo.each(queue, function(box, k){
33615             
33616             var col = k % this.cols;
33617             
33618             Roo.each(box, function(b,kk){
33619                 
33620                 b.el.position('absolute');
33621                 
33622                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33623                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33624                 
33625                 if(b.size == 'md-left' || b.size == 'md-right'){
33626                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33627                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33628                 }
33629                 
33630                 b.el.setWidth(width);
33631                 b.el.setHeight(height);
33632                 // iframe?
33633                 b.el.select('iframe',true).setSize(width,height);
33634                 
33635             }, this);
33636             
33637             for (var i = 0; i < this.cols; i++){
33638                 
33639                 if(maxY[i] < maxY[col]){
33640                     col = i;
33641                     continue;
33642                 }
33643                 
33644                 col = Math.min(col, i);
33645                 
33646             }
33647             
33648             x = pos.x + col * (this.colWidth + this.padWidth);
33649             
33650             y = maxY[col];
33651             
33652             var positions = [];
33653             
33654             switch (box.length){
33655                 case 1 :
33656                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33657                     break;
33658                 case 2 :
33659                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33660                     break;
33661                 case 3 :
33662                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33663                     break;
33664                 case 4 :
33665                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33666                     break;
33667                 default :
33668                     break;
33669             }
33670             
33671             Roo.each(box, function(b,kk){
33672                 
33673                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33674                 
33675                 var sz = b.el.getSize();
33676                 
33677                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33678                 
33679             }, this);
33680             
33681         }, this);
33682         
33683         var mY = 0;
33684         
33685         for (var i = 0; i < this.cols; i++){
33686             mY = Math.max(mY, maxY[i]);
33687         }
33688         
33689         this.el.setHeight(mY - pos.y);
33690         
33691     },
33692     
33693 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33694 //    {
33695 //        var pos = this.el.getBox(true);
33696 //        var x = pos.x;
33697 //        var y = pos.y;
33698 //        var maxX = pos.right;
33699 //        
33700 //        var maxHeight = 0;
33701 //        
33702 //        Roo.each(items, function(item, k){
33703 //            
33704 //            var c = k % 2;
33705 //            
33706 //            item.el.position('absolute');
33707 //                
33708 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33709 //
33710 //            item.el.setWidth(width);
33711 //
33712 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33713 //
33714 //            item.el.setHeight(height);
33715 //            
33716 //            if(c == 0){
33717 //                item.el.setXY([x, y], isInstant ? false : true);
33718 //            } else {
33719 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33720 //            }
33721 //            
33722 //            y = y + height + this.alternativePadWidth;
33723 //            
33724 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33725 //            
33726 //        }, this);
33727 //        
33728 //        this.el.setHeight(maxHeight);
33729 //        
33730 //    },
33731     
33732     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33733     {
33734         var pos = this.el.getBox(true);
33735         
33736         var minX = pos.x;
33737         var minY = pos.y;
33738         
33739         var maxX = pos.right;
33740         
33741         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33742         
33743         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33744         
33745         Roo.each(queue, function(box, k){
33746             
33747             Roo.each(box, function(b, kk){
33748                 
33749                 b.el.position('absolute');
33750                 
33751                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33752                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33753                 
33754                 if(b.size == 'md-left' || b.size == 'md-right'){
33755                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33756                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33757                 }
33758                 
33759                 b.el.setWidth(width);
33760                 b.el.setHeight(height);
33761                 
33762             }, this);
33763             
33764             if(!box.length){
33765                 return;
33766             }
33767             
33768             var positions = [];
33769             
33770             switch (box.length){
33771                 case 1 :
33772                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33773                     break;
33774                 case 2 :
33775                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33776                     break;
33777                 case 3 :
33778                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33779                     break;
33780                 case 4 :
33781                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33782                     break;
33783                 default :
33784                     break;
33785             }
33786             
33787             Roo.each(box, function(b,kk){
33788                 
33789                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33790                 
33791                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33792                 
33793             }, this);
33794             
33795         }, this);
33796         
33797     },
33798     
33799     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33800     {
33801         Roo.each(eItems, function(b,k){
33802             
33803             b.size = (k == 0) ? 'sm' : 'xs';
33804             b.x = (k == 0) ? 2 : 1;
33805             b.y = (k == 0) ? 2 : 1;
33806             
33807             b.el.position('absolute');
33808             
33809             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33810                 
33811             b.el.setWidth(width);
33812             
33813             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33814             
33815             b.el.setHeight(height);
33816             
33817         }, this);
33818
33819         var positions = [];
33820         
33821         positions.push({
33822             x : maxX - this.unitWidth * 2 - this.gutter,
33823             y : minY
33824         });
33825         
33826         positions.push({
33827             x : maxX - this.unitWidth,
33828             y : minY + (this.unitWidth + this.gutter) * 2
33829         });
33830         
33831         positions.push({
33832             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33833             y : minY
33834         });
33835         
33836         Roo.each(eItems, function(b,k){
33837             
33838             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33839
33840         }, this);
33841         
33842     },
33843     
33844     getVerticalOneBoxColPositions : function(x, y, box)
33845     {
33846         var pos = [];
33847         
33848         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33849         
33850         if(box[0].size == 'md-left'){
33851             rand = 0;
33852         }
33853         
33854         if(box[0].size == 'md-right'){
33855             rand = 1;
33856         }
33857         
33858         pos.push({
33859             x : x + (this.unitWidth + this.gutter) * rand,
33860             y : y
33861         });
33862         
33863         return pos;
33864     },
33865     
33866     getVerticalTwoBoxColPositions : function(x, y, box)
33867     {
33868         var pos = [];
33869         
33870         if(box[0].size == 'xs'){
33871             
33872             pos.push({
33873                 x : x,
33874                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33875             });
33876
33877             pos.push({
33878                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33879                 y : y
33880             });
33881             
33882             return pos;
33883             
33884         }
33885         
33886         pos.push({
33887             x : x,
33888             y : y
33889         });
33890
33891         pos.push({
33892             x : x + (this.unitWidth + this.gutter) * 2,
33893             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33894         });
33895         
33896         return pos;
33897         
33898     },
33899     
33900     getVerticalThreeBoxColPositions : function(x, y, box)
33901     {
33902         var pos = [];
33903         
33904         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33905             
33906             pos.push({
33907                 x : x,
33908                 y : y
33909             });
33910
33911             pos.push({
33912                 x : x + (this.unitWidth + this.gutter) * 1,
33913                 y : y
33914             });
33915             
33916             pos.push({
33917                 x : x + (this.unitWidth + this.gutter) * 2,
33918                 y : y
33919             });
33920             
33921             return pos;
33922             
33923         }
33924         
33925         if(box[0].size == 'xs' && box[1].size == 'xs'){
33926             
33927             pos.push({
33928                 x : x,
33929                 y : y
33930             });
33931
33932             pos.push({
33933                 x : x,
33934                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33935             });
33936             
33937             pos.push({
33938                 x : x + (this.unitWidth + this.gutter) * 1,
33939                 y : y
33940             });
33941             
33942             return pos;
33943             
33944         }
33945         
33946         pos.push({
33947             x : x,
33948             y : y
33949         });
33950
33951         pos.push({
33952             x : x + (this.unitWidth + this.gutter) * 2,
33953             y : y
33954         });
33955
33956         pos.push({
33957             x : x + (this.unitWidth + this.gutter) * 2,
33958             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33959         });
33960             
33961         return pos;
33962         
33963     },
33964     
33965     getVerticalFourBoxColPositions : function(x, y, box)
33966     {
33967         var pos = [];
33968         
33969         if(box[0].size == 'xs'){
33970             
33971             pos.push({
33972                 x : x,
33973                 y : y
33974             });
33975
33976             pos.push({
33977                 x : x,
33978                 y : y + (this.unitHeight + this.gutter) * 1
33979             });
33980             
33981             pos.push({
33982                 x : x,
33983                 y : y + (this.unitHeight + this.gutter) * 2
33984             });
33985             
33986             pos.push({
33987                 x : x + (this.unitWidth + this.gutter) * 1,
33988                 y : y
33989             });
33990             
33991             return pos;
33992             
33993         }
33994         
33995         pos.push({
33996             x : x,
33997             y : y
33998         });
33999
34000         pos.push({
34001             x : x + (this.unitWidth + this.gutter) * 2,
34002             y : y
34003         });
34004
34005         pos.push({
34006             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34007             y : y + (this.unitHeight + this.gutter) * 1
34008         });
34009
34010         pos.push({
34011             x : x + (this.unitWidth + this.gutter) * 2,
34012             y : y + (this.unitWidth + this.gutter) * 2
34013         });
34014
34015         return pos;
34016         
34017     },
34018     
34019     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34020     {
34021         var pos = [];
34022         
34023         if(box[0].size == 'md-left'){
34024             pos.push({
34025                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34026                 y : minY
34027             });
34028             
34029             return pos;
34030         }
34031         
34032         if(box[0].size == 'md-right'){
34033             pos.push({
34034                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34035                 y : minY + (this.unitWidth + this.gutter) * 1
34036             });
34037             
34038             return pos;
34039         }
34040         
34041         var rand = Math.floor(Math.random() * (4 - box[0].y));
34042         
34043         pos.push({
34044             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34045             y : minY + (this.unitWidth + this.gutter) * rand
34046         });
34047         
34048         return pos;
34049         
34050     },
34051     
34052     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34053     {
34054         var pos = [];
34055         
34056         if(box[0].size == 'xs'){
34057             
34058             pos.push({
34059                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34060                 y : minY
34061             });
34062
34063             pos.push({
34064                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34065                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34066             });
34067             
34068             return pos;
34069             
34070         }
34071         
34072         pos.push({
34073             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34074             y : minY
34075         });
34076
34077         pos.push({
34078             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34079             y : minY + (this.unitWidth + this.gutter) * 2
34080         });
34081         
34082         return pos;
34083         
34084     },
34085     
34086     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34087     {
34088         var pos = [];
34089         
34090         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34091             
34092             pos.push({
34093                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34094                 y : minY
34095             });
34096
34097             pos.push({
34098                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34099                 y : minY + (this.unitWidth + this.gutter) * 1
34100             });
34101             
34102             pos.push({
34103                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34104                 y : minY + (this.unitWidth + this.gutter) * 2
34105             });
34106             
34107             return pos;
34108             
34109         }
34110         
34111         if(box[0].size == 'xs' && box[1].size == 'xs'){
34112             
34113             pos.push({
34114                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34115                 y : minY
34116             });
34117
34118             pos.push({
34119                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34120                 y : minY
34121             });
34122             
34123             pos.push({
34124                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34125                 y : minY + (this.unitWidth + this.gutter) * 1
34126             });
34127             
34128             return pos;
34129             
34130         }
34131         
34132         pos.push({
34133             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34134             y : minY
34135         });
34136
34137         pos.push({
34138             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34139             y : minY + (this.unitWidth + this.gutter) * 2
34140         });
34141
34142         pos.push({
34143             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34144             y : minY + (this.unitWidth + this.gutter) * 2
34145         });
34146             
34147         return pos;
34148         
34149     },
34150     
34151     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34152     {
34153         var pos = [];
34154         
34155         if(box[0].size == 'xs'){
34156             
34157             pos.push({
34158                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34159                 y : minY
34160             });
34161
34162             pos.push({
34163                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34164                 y : minY
34165             });
34166             
34167             pos.push({
34168                 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),
34169                 y : minY
34170             });
34171             
34172             pos.push({
34173                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34174                 y : minY + (this.unitWidth + this.gutter) * 1
34175             });
34176             
34177             return pos;
34178             
34179         }
34180         
34181         pos.push({
34182             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34183             y : minY
34184         });
34185         
34186         pos.push({
34187             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34188             y : minY + (this.unitWidth + this.gutter) * 2
34189         });
34190         
34191         pos.push({
34192             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34193             y : minY + (this.unitWidth + this.gutter) * 2
34194         });
34195         
34196         pos.push({
34197             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),
34198             y : minY + (this.unitWidth + this.gutter) * 2
34199         });
34200
34201         return pos;
34202         
34203     },
34204     
34205     /**
34206     * remove a Masonry Brick
34207     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34208     */
34209     removeBrick : function(brick_id)
34210     {
34211         if (!brick_id) {
34212             return;
34213         }
34214         
34215         for (var i = 0; i<this.bricks.length; i++) {
34216             if (this.bricks[i].id == brick_id) {
34217                 this.bricks.splice(i,1);
34218                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34219                 this.initial();
34220             }
34221         }
34222     },
34223     
34224     /**
34225     * adds a Masonry Brick
34226     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34227     */
34228     addBrick : function(cfg)
34229     {
34230         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34231         //this.register(cn);
34232         cn.parentId = this.id;
34233         cn.render(this.el);
34234         return cn;
34235     },
34236     
34237     /**
34238     * register a Masonry Brick
34239     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34240     */
34241     
34242     register : function(brick)
34243     {
34244         this.bricks.push(brick);
34245         brick.masonryId = this.id;
34246     },
34247     
34248     /**
34249     * clear all the Masonry Brick
34250     */
34251     clearAll : function()
34252     {
34253         this.bricks = [];
34254         //this.getChildContainer().dom.innerHTML = "";
34255         this.el.dom.innerHTML = '';
34256     },
34257     
34258     getSelected : function()
34259     {
34260         if (!this.selectedBrick) {
34261             return false;
34262         }
34263         
34264         return this.selectedBrick;
34265     }
34266 });
34267
34268 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34269     
34270     groups: {},
34271      /**
34272     * register a Masonry Layout
34273     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34274     */
34275     
34276     register : function(layout)
34277     {
34278         this.groups[layout.id] = layout;
34279     },
34280     /**
34281     * fetch a  Masonry Layout based on the masonry layout ID
34282     * @param {string} the masonry layout to add
34283     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34284     */
34285     
34286     get: function(layout_id) {
34287         if (typeof(this.groups[layout_id]) == 'undefined') {
34288             return false;
34289         }
34290         return this.groups[layout_id] ;
34291     }
34292     
34293     
34294     
34295 });
34296
34297  
34298
34299  /**
34300  *
34301  * This is based on 
34302  * http://masonry.desandro.com
34303  *
34304  * The idea is to render all the bricks based on vertical width...
34305  *
34306  * The original code extends 'outlayer' - we might need to use that....
34307  * 
34308  */
34309
34310
34311 /**
34312  * @class Roo.bootstrap.LayoutMasonryAuto
34313  * @extends Roo.bootstrap.Component
34314  * Bootstrap Layout Masonry class
34315  * 
34316  * @constructor
34317  * Create a new Element
34318  * @param {Object} config The config object
34319  */
34320
34321 Roo.bootstrap.LayoutMasonryAuto = function(config){
34322     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34323 };
34324
34325 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34326     
34327       /**
34328      * @cfg {Boolean} isFitWidth  - resize the width..
34329      */   
34330     isFitWidth : false,  // options..
34331     /**
34332      * @cfg {Boolean} isOriginLeft = left align?
34333      */   
34334     isOriginLeft : true,
34335     /**
34336      * @cfg {Boolean} isOriginTop = top align?
34337      */   
34338     isOriginTop : false,
34339     /**
34340      * @cfg {Boolean} isLayoutInstant = no animation?
34341      */   
34342     isLayoutInstant : false, // needed?
34343     /**
34344      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34345      */   
34346     isResizingContainer : true,
34347     /**
34348      * @cfg {Number} columnWidth  width of the columns 
34349      */   
34350     
34351     columnWidth : 0,
34352     
34353     /**
34354      * @cfg {Number} maxCols maximum number of columns
34355      */   
34356     
34357     maxCols: 0,
34358     /**
34359      * @cfg {Number} padHeight padding below box..
34360      */   
34361     
34362     padHeight : 10, 
34363     
34364     /**
34365      * @cfg {Boolean} isAutoInitial defalut true
34366      */   
34367     
34368     isAutoInitial : true, 
34369     
34370     // private?
34371     gutter : 0,
34372     
34373     containerWidth: 0,
34374     initialColumnWidth : 0,
34375     currentSize : null,
34376     
34377     colYs : null, // array.
34378     maxY : 0,
34379     padWidth: 10,
34380     
34381     
34382     tag: 'div',
34383     cls: '',
34384     bricks: null, //CompositeElement
34385     cols : 0, // array?
34386     // element : null, // wrapped now this.el
34387     _isLayoutInited : null, 
34388     
34389     
34390     getAutoCreate : function(){
34391         
34392         var cfg = {
34393             tag: this.tag,
34394             cls: 'blog-masonary-wrapper ' + this.cls,
34395             cn : {
34396                 cls : 'mas-boxes masonary'
34397             }
34398         };
34399         
34400         return cfg;
34401     },
34402     
34403     getChildContainer: function( )
34404     {
34405         if (this.boxesEl) {
34406             return this.boxesEl;
34407         }
34408         
34409         this.boxesEl = this.el.select('.mas-boxes').first();
34410         
34411         return this.boxesEl;
34412     },
34413     
34414     
34415     initEvents : function()
34416     {
34417         var _this = this;
34418         
34419         if(this.isAutoInitial){
34420             Roo.log('hook children rendered');
34421             this.on('childrenrendered', function() {
34422                 Roo.log('children rendered');
34423                 _this.initial();
34424             } ,this);
34425         }
34426         
34427     },
34428     
34429     initial : function()
34430     {
34431         this.reloadItems();
34432
34433         this.currentSize = this.el.getBox(true);
34434
34435         /// was window resize... - let's see if this works..
34436         Roo.EventManager.onWindowResize(this.resize, this); 
34437
34438         if(!this.isAutoInitial){
34439             this.layout();
34440             return;
34441         }
34442         
34443         this.layout.defer(500,this);
34444     },
34445     
34446     reloadItems: function()
34447     {
34448         this.bricks = this.el.select('.masonry-brick', true);
34449         
34450         this.bricks.each(function(b) {
34451             //Roo.log(b.getSize());
34452             if (!b.attr('originalwidth')) {
34453                 b.attr('originalwidth',  b.getSize().width);
34454             }
34455             
34456         });
34457         
34458         Roo.log(this.bricks.elements.length);
34459     },
34460     
34461     resize : function()
34462     {
34463         Roo.log('resize');
34464         var cs = this.el.getBox(true);
34465         
34466         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34467             Roo.log("no change in with or X");
34468             return;
34469         }
34470         this.currentSize = cs;
34471         this.layout();
34472     },
34473     
34474     layout : function()
34475     {
34476          Roo.log('layout');
34477         this._resetLayout();
34478         //this._manageStamps();
34479       
34480         // don't animate first layout
34481         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34482         this.layoutItems( isInstant );
34483       
34484         // flag for initalized
34485         this._isLayoutInited = true;
34486     },
34487     
34488     layoutItems : function( isInstant )
34489     {
34490         //var items = this._getItemsForLayout( this.items );
34491         // original code supports filtering layout items.. we just ignore it..
34492         
34493         this._layoutItems( this.bricks , isInstant );
34494       
34495         this._postLayout();
34496     },
34497     _layoutItems : function ( items , isInstant)
34498     {
34499        //this.fireEvent( 'layout', this, items );
34500     
34501
34502         if ( !items || !items.elements.length ) {
34503           // no items, emit event with empty array
34504             return;
34505         }
34506
34507         var queue = [];
34508         items.each(function(item) {
34509             Roo.log("layout item");
34510             Roo.log(item);
34511             // get x/y object from method
34512             var position = this._getItemLayoutPosition( item );
34513             // enqueue
34514             position.item = item;
34515             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34516             queue.push( position );
34517         }, this);
34518       
34519         this._processLayoutQueue( queue );
34520     },
34521     /** Sets position of item in DOM
34522     * @param {Element} item
34523     * @param {Number} x - horizontal position
34524     * @param {Number} y - vertical position
34525     * @param {Boolean} isInstant - disables transitions
34526     */
34527     _processLayoutQueue : function( queue )
34528     {
34529         for ( var i=0, len = queue.length; i < len; i++ ) {
34530             var obj = queue[i];
34531             obj.item.position('absolute');
34532             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34533         }
34534     },
34535       
34536     
34537     /**
34538     * Any logic you want to do after each layout,
34539     * i.e. size the container
34540     */
34541     _postLayout : function()
34542     {
34543         this.resizeContainer();
34544     },
34545     
34546     resizeContainer : function()
34547     {
34548         if ( !this.isResizingContainer ) {
34549             return;
34550         }
34551         var size = this._getContainerSize();
34552         if ( size ) {
34553             this.el.setSize(size.width,size.height);
34554             this.boxesEl.setSize(size.width,size.height);
34555         }
34556     },
34557     
34558     
34559     
34560     _resetLayout : function()
34561     {
34562         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34563         this.colWidth = this.el.getWidth();
34564         //this.gutter = this.el.getWidth(); 
34565         
34566         this.measureColumns();
34567
34568         // reset column Y
34569         var i = this.cols;
34570         this.colYs = [];
34571         while (i--) {
34572             this.colYs.push( 0 );
34573         }
34574     
34575         this.maxY = 0;
34576     },
34577
34578     measureColumns : function()
34579     {
34580         this.getContainerWidth();
34581       // if columnWidth is 0, default to outerWidth of first item
34582         if ( !this.columnWidth ) {
34583             var firstItem = this.bricks.first();
34584             Roo.log(firstItem);
34585             this.columnWidth  = this.containerWidth;
34586             if (firstItem && firstItem.attr('originalwidth') ) {
34587                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34588             }
34589             // columnWidth fall back to item of first element
34590             Roo.log("set column width?");
34591                         this.initialColumnWidth = this.columnWidth  ;
34592
34593             // if first elem has no width, default to size of container
34594             
34595         }
34596         
34597         
34598         if (this.initialColumnWidth) {
34599             this.columnWidth = this.initialColumnWidth;
34600         }
34601         
34602         
34603             
34604         // column width is fixed at the top - however if container width get's smaller we should
34605         // reduce it...
34606         
34607         // this bit calcs how man columns..
34608             
34609         var columnWidth = this.columnWidth += this.gutter;
34610       
34611         // calculate columns
34612         var containerWidth = this.containerWidth + this.gutter;
34613         
34614         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34615         // fix rounding errors, typically with gutters
34616         var excess = columnWidth - containerWidth % columnWidth;
34617         
34618         
34619         // if overshoot is less than a pixel, round up, otherwise floor it
34620         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34621         cols = Math[ mathMethod ]( cols );
34622         this.cols = Math.max( cols, 1 );
34623         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34624         
34625          // padding positioning..
34626         var totalColWidth = this.cols * this.columnWidth;
34627         var padavail = this.containerWidth - totalColWidth;
34628         // so for 2 columns - we need 3 'pads'
34629         
34630         var padNeeded = (1+this.cols) * this.padWidth;
34631         
34632         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34633         
34634         this.columnWidth += padExtra
34635         //this.padWidth = Math.floor(padavail /  ( this.cols));
34636         
34637         // adjust colum width so that padding is fixed??
34638         
34639         // we have 3 columns ... total = width * 3
34640         // we have X left over... that should be used by 
34641         
34642         //if (this.expandC) {
34643             
34644         //}
34645         
34646         
34647         
34648     },
34649     
34650     getContainerWidth : function()
34651     {
34652        /* // container is parent if fit width
34653         var container = this.isFitWidth ? this.element.parentNode : this.element;
34654         // check that this.size and size are there
34655         // IE8 triggers resize on body size change, so they might not be
34656         
34657         var size = getSize( container );  //FIXME
34658         this.containerWidth = size && size.innerWidth; //FIXME
34659         */
34660          
34661         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34662         
34663     },
34664     
34665     _getItemLayoutPosition : function( item )  // what is item?
34666     {
34667         // we resize the item to our columnWidth..
34668       
34669         item.setWidth(this.columnWidth);
34670         item.autoBoxAdjust  = false;
34671         
34672         var sz = item.getSize();
34673  
34674         // how many columns does this brick span
34675         var remainder = this.containerWidth % this.columnWidth;
34676         
34677         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34678         // round if off by 1 pixel, otherwise use ceil
34679         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34680         colSpan = Math.min( colSpan, this.cols );
34681         
34682         // normally this should be '1' as we dont' currently allow multi width columns..
34683         
34684         var colGroup = this._getColGroup( colSpan );
34685         // get the minimum Y value from the columns
34686         var minimumY = Math.min.apply( Math, colGroup );
34687         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34688         
34689         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34690          
34691         // position the brick
34692         var position = {
34693             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34694             y: this.currentSize.y + minimumY + this.padHeight
34695         };
34696         
34697         Roo.log(position);
34698         // apply setHeight to necessary columns
34699         var setHeight = minimumY + sz.height + this.padHeight;
34700         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34701         
34702         var setSpan = this.cols + 1 - colGroup.length;
34703         for ( var i = 0; i < setSpan; i++ ) {
34704           this.colYs[ shortColIndex + i ] = setHeight ;
34705         }
34706       
34707         return position;
34708     },
34709     
34710     /**
34711      * @param {Number} colSpan - number of columns the element spans
34712      * @returns {Array} colGroup
34713      */
34714     _getColGroup : function( colSpan )
34715     {
34716         if ( colSpan < 2 ) {
34717           // if brick spans only one column, use all the column Ys
34718           return this.colYs;
34719         }
34720       
34721         var colGroup = [];
34722         // how many different places could this brick fit horizontally
34723         var groupCount = this.cols + 1 - colSpan;
34724         // for each group potential horizontal position
34725         for ( var i = 0; i < groupCount; i++ ) {
34726           // make an array of colY values for that one group
34727           var groupColYs = this.colYs.slice( i, i + colSpan );
34728           // and get the max value of the array
34729           colGroup[i] = Math.max.apply( Math, groupColYs );
34730         }
34731         return colGroup;
34732     },
34733     /*
34734     _manageStamp : function( stamp )
34735     {
34736         var stampSize =  stamp.getSize();
34737         var offset = stamp.getBox();
34738         // get the columns that this stamp affects
34739         var firstX = this.isOriginLeft ? offset.x : offset.right;
34740         var lastX = firstX + stampSize.width;
34741         var firstCol = Math.floor( firstX / this.columnWidth );
34742         firstCol = Math.max( 0, firstCol );
34743         
34744         var lastCol = Math.floor( lastX / this.columnWidth );
34745         // lastCol should not go over if multiple of columnWidth #425
34746         lastCol -= lastX % this.columnWidth ? 0 : 1;
34747         lastCol = Math.min( this.cols - 1, lastCol );
34748         
34749         // set colYs to bottom of the stamp
34750         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34751             stampSize.height;
34752             
34753         for ( var i = firstCol; i <= lastCol; i++ ) {
34754           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34755         }
34756     },
34757     */
34758     
34759     _getContainerSize : function()
34760     {
34761         this.maxY = Math.max.apply( Math, this.colYs );
34762         var size = {
34763             height: this.maxY
34764         };
34765       
34766         if ( this.isFitWidth ) {
34767             size.width = this._getContainerFitWidth();
34768         }
34769       
34770         return size;
34771     },
34772     
34773     _getContainerFitWidth : function()
34774     {
34775         var unusedCols = 0;
34776         // count unused columns
34777         var i = this.cols;
34778         while ( --i ) {
34779           if ( this.colYs[i] !== 0 ) {
34780             break;
34781           }
34782           unusedCols++;
34783         }
34784         // fit container to columns that have been used
34785         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34786     },
34787     
34788     needsResizeLayout : function()
34789     {
34790         var previousWidth = this.containerWidth;
34791         this.getContainerWidth();
34792         return previousWidth !== this.containerWidth;
34793     }
34794  
34795 });
34796
34797  
34798
34799  /*
34800  * - LGPL
34801  *
34802  * element
34803  * 
34804  */
34805
34806 /**
34807  * @class Roo.bootstrap.MasonryBrick
34808  * @extends Roo.bootstrap.Component
34809  * Bootstrap MasonryBrick class
34810  * 
34811  * @constructor
34812  * Create a new MasonryBrick
34813  * @param {Object} config The config object
34814  */
34815
34816 Roo.bootstrap.MasonryBrick = function(config){
34817     
34818     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34819     
34820     Roo.bootstrap.MasonryBrick.register(this);
34821     
34822     this.addEvents({
34823         // raw events
34824         /**
34825          * @event click
34826          * When a MasonryBrick is clcik
34827          * @param {Roo.bootstrap.MasonryBrick} this
34828          * @param {Roo.EventObject} e
34829          */
34830         "click" : true
34831     });
34832 };
34833
34834 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34835     
34836     /**
34837      * @cfg {String} title
34838      */   
34839     title : '',
34840     /**
34841      * @cfg {String} html
34842      */   
34843     html : '',
34844     /**
34845      * @cfg {String} bgimage
34846      */   
34847     bgimage : '',
34848     /**
34849      * @cfg {String} videourl
34850      */   
34851     videourl : '',
34852     /**
34853      * @cfg {String} cls
34854      */   
34855     cls : '',
34856     /**
34857      * @cfg {String} href
34858      */   
34859     href : '',
34860     /**
34861      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34862      */   
34863     size : 'xs',
34864     
34865     /**
34866      * @cfg {String} placetitle (center|bottom)
34867      */   
34868     placetitle : '',
34869     
34870     /**
34871      * @cfg {Boolean} isFitContainer defalut true
34872      */   
34873     isFitContainer : true, 
34874     
34875     /**
34876      * @cfg {Boolean} preventDefault defalut false
34877      */   
34878     preventDefault : false, 
34879     
34880     /**
34881      * @cfg {Boolean} inverse defalut false
34882      */   
34883     maskInverse : false, 
34884     
34885     getAutoCreate : function()
34886     {
34887         if(!this.isFitContainer){
34888             return this.getSplitAutoCreate();
34889         }
34890         
34891         var cls = 'masonry-brick masonry-brick-full';
34892         
34893         if(this.href.length){
34894             cls += ' masonry-brick-link';
34895         }
34896         
34897         if(this.bgimage.length){
34898             cls += ' masonry-brick-image';
34899         }
34900         
34901         if(this.maskInverse){
34902             cls += ' mask-inverse';
34903         }
34904         
34905         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34906             cls += ' enable-mask';
34907         }
34908         
34909         if(this.size){
34910             cls += ' masonry-' + this.size + '-brick';
34911         }
34912         
34913         if(this.placetitle.length){
34914             
34915             switch (this.placetitle) {
34916                 case 'center' :
34917                     cls += ' masonry-center-title';
34918                     break;
34919                 case 'bottom' :
34920                     cls += ' masonry-bottom-title';
34921                     break;
34922                 default:
34923                     break;
34924             }
34925             
34926         } else {
34927             if(!this.html.length && !this.bgimage.length){
34928                 cls += ' masonry-center-title';
34929             }
34930
34931             if(!this.html.length && this.bgimage.length){
34932                 cls += ' masonry-bottom-title';
34933             }
34934         }
34935         
34936         if(this.cls){
34937             cls += ' ' + this.cls;
34938         }
34939         
34940         var cfg = {
34941             tag: (this.href.length) ? 'a' : 'div',
34942             cls: cls,
34943             cn: [
34944                 {
34945                     tag: 'div',
34946                     cls: 'masonry-brick-mask'
34947                 },
34948                 {
34949                     tag: 'div',
34950                     cls: 'masonry-brick-paragraph',
34951                     cn: []
34952                 }
34953             ]
34954         };
34955         
34956         if(this.href.length){
34957             cfg.href = this.href;
34958         }
34959         
34960         var cn = cfg.cn[1].cn;
34961         
34962         if(this.title.length){
34963             cn.push({
34964                 tag: 'h4',
34965                 cls: 'masonry-brick-title',
34966                 html: this.title
34967             });
34968         }
34969         
34970         if(this.html.length){
34971             cn.push({
34972                 tag: 'p',
34973                 cls: 'masonry-brick-text',
34974                 html: this.html
34975             });
34976         }
34977         
34978         if (!this.title.length && !this.html.length) {
34979             cfg.cn[1].cls += ' hide';
34980         }
34981         
34982         if(this.bgimage.length){
34983             cfg.cn.push({
34984                 tag: 'img',
34985                 cls: 'masonry-brick-image-view',
34986                 src: this.bgimage
34987             });
34988         }
34989         
34990         if(this.videourl.length){
34991             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34992             // youtube support only?
34993             cfg.cn.push({
34994                 tag: 'iframe',
34995                 cls: 'masonry-brick-image-view',
34996                 src: vurl,
34997                 frameborder : 0,
34998                 allowfullscreen : true
34999             });
35000         }
35001         
35002         return cfg;
35003         
35004     },
35005     
35006     getSplitAutoCreate : function()
35007     {
35008         var cls = 'masonry-brick masonry-brick-split';
35009         
35010         if(this.href.length){
35011             cls += ' masonry-brick-link';
35012         }
35013         
35014         if(this.bgimage.length){
35015             cls += ' masonry-brick-image';
35016         }
35017         
35018         if(this.size){
35019             cls += ' masonry-' + this.size + '-brick';
35020         }
35021         
35022         switch (this.placetitle) {
35023             case 'center' :
35024                 cls += ' masonry-center-title';
35025                 break;
35026             case 'bottom' :
35027                 cls += ' masonry-bottom-title';
35028                 break;
35029             default:
35030                 if(!this.bgimage.length){
35031                     cls += ' masonry-center-title';
35032                 }
35033
35034                 if(this.bgimage.length){
35035                     cls += ' masonry-bottom-title';
35036                 }
35037                 break;
35038         }
35039         
35040         if(this.cls){
35041             cls += ' ' + this.cls;
35042         }
35043         
35044         var cfg = {
35045             tag: (this.href.length) ? 'a' : 'div',
35046             cls: cls,
35047             cn: [
35048                 {
35049                     tag: 'div',
35050                     cls: 'masonry-brick-split-head',
35051                     cn: [
35052                         {
35053                             tag: 'div',
35054                             cls: 'masonry-brick-paragraph',
35055                             cn: []
35056                         }
35057                     ]
35058                 },
35059                 {
35060                     tag: 'div',
35061                     cls: 'masonry-brick-split-body',
35062                     cn: []
35063                 }
35064             ]
35065         };
35066         
35067         if(this.href.length){
35068             cfg.href = this.href;
35069         }
35070         
35071         if(this.title.length){
35072             cfg.cn[0].cn[0].cn.push({
35073                 tag: 'h4',
35074                 cls: 'masonry-brick-title',
35075                 html: this.title
35076             });
35077         }
35078         
35079         if(this.html.length){
35080             cfg.cn[1].cn.push({
35081                 tag: 'p',
35082                 cls: 'masonry-brick-text',
35083                 html: this.html
35084             });
35085         }
35086
35087         if(this.bgimage.length){
35088             cfg.cn[0].cn.push({
35089                 tag: 'img',
35090                 cls: 'masonry-brick-image-view',
35091                 src: this.bgimage
35092             });
35093         }
35094         
35095         if(this.videourl.length){
35096             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35097             // youtube support only?
35098             cfg.cn[0].cn.cn.push({
35099                 tag: 'iframe',
35100                 cls: 'masonry-brick-image-view',
35101                 src: vurl,
35102                 frameborder : 0,
35103                 allowfullscreen : true
35104             });
35105         }
35106         
35107         return cfg;
35108     },
35109     
35110     initEvents: function() 
35111     {
35112         switch (this.size) {
35113             case 'xs' :
35114                 this.x = 1;
35115                 this.y = 1;
35116                 break;
35117             case 'sm' :
35118                 this.x = 2;
35119                 this.y = 2;
35120                 break;
35121             case 'md' :
35122             case 'md-left' :
35123             case 'md-right' :
35124                 this.x = 3;
35125                 this.y = 3;
35126                 break;
35127             case 'tall' :
35128                 this.x = 2;
35129                 this.y = 3;
35130                 break;
35131             case 'wide' :
35132                 this.x = 3;
35133                 this.y = 2;
35134                 break;
35135             case 'wide-thin' :
35136                 this.x = 3;
35137                 this.y = 1;
35138                 break;
35139                         
35140             default :
35141                 break;
35142         }
35143         
35144         if(Roo.isTouch){
35145             this.el.on('touchstart', this.onTouchStart, this);
35146             this.el.on('touchmove', this.onTouchMove, this);
35147             this.el.on('touchend', this.onTouchEnd, this);
35148             this.el.on('contextmenu', this.onContextMenu, this);
35149         } else {
35150             this.el.on('mouseenter'  ,this.enter, this);
35151             this.el.on('mouseleave', this.leave, this);
35152             this.el.on('click', this.onClick, this);
35153         }
35154         
35155         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35156             this.parent().bricks.push(this);   
35157         }
35158         
35159     },
35160     
35161     onClick: function(e, el)
35162     {
35163         var time = this.endTimer - this.startTimer;
35164         // Roo.log(e.preventDefault());
35165         if(Roo.isTouch){
35166             if(time > 1000){
35167                 e.preventDefault();
35168                 return;
35169             }
35170         }
35171         
35172         if(!this.preventDefault){
35173             return;
35174         }
35175         
35176         e.preventDefault();
35177         
35178         if (this.activeClass != '') {
35179             this.selectBrick();
35180         }
35181         
35182         this.fireEvent('click', this, e);
35183     },
35184     
35185     enter: function(e, el)
35186     {
35187         e.preventDefault();
35188         
35189         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35190             return;
35191         }
35192         
35193         if(this.bgimage.length && this.html.length){
35194             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35195         }
35196     },
35197     
35198     leave: function(e, el)
35199     {
35200         e.preventDefault();
35201         
35202         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35203             return;
35204         }
35205         
35206         if(this.bgimage.length && this.html.length){
35207             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35208         }
35209     },
35210     
35211     onTouchStart: function(e, el)
35212     {
35213 //        e.preventDefault();
35214         
35215         this.touchmoved = false;
35216         
35217         if(!this.isFitContainer){
35218             return;
35219         }
35220         
35221         if(!this.bgimage.length || !this.html.length){
35222             return;
35223         }
35224         
35225         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35226         
35227         this.timer = new Date().getTime();
35228         
35229     },
35230     
35231     onTouchMove: function(e, el)
35232     {
35233         this.touchmoved = true;
35234     },
35235     
35236     onContextMenu : function(e,el)
35237     {
35238         e.preventDefault();
35239         e.stopPropagation();
35240         return false;
35241     },
35242     
35243     onTouchEnd: function(e, el)
35244     {
35245 //        e.preventDefault();
35246         
35247         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35248         
35249             this.leave(e,el);
35250             
35251             return;
35252         }
35253         
35254         if(!this.bgimage.length || !this.html.length){
35255             
35256             if(this.href.length){
35257                 window.location.href = this.href;
35258             }
35259             
35260             return;
35261         }
35262         
35263         if(!this.isFitContainer){
35264             return;
35265         }
35266         
35267         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35268         
35269         window.location.href = this.href;
35270     },
35271     
35272     //selection on single brick only
35273     selectBrick : function() {
35274         
35275         if (!this.parentId) {
35276             return;
35277         }
35278         
35279         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35280         var index = m.selectedBrick.indexOf(this.id);
35281         
35282         if ( index > -1) {
35283             m.selectedBrick.splice(index,1);
35284             this.el.removeClass(this.activeClass);
35285             return;
35286         }
35287         
35288         for(var i = 0; i < m.selectedBrick.length; i++) {
35289             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35290             b.el.removeClass(b.activeClass);
35291         }
35292         
35293         m.selectedBrick = [];
35294         
35295         m.selectedBrick.push(this.id);
35296         this.el.addClass(this.activeClass);
35297         return;
35298     },
35299     
35300     isSelected : function(){
35301         return this.el.hasClass(this.activeClass);
35302         
35303     }
35304 });
35305
35306 Roo.apply(Roo.bootstrap.MasonryBrick, {
35307     
35308     //groups: {},
35309     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35310      /**
35311     * register a Masonry Brick
35312     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35313     */
35314     
35315     register : function(brick)
35316     {
35317         //this.groups[brick.id] = brick;
35318         this.groups.add(brick.id, brick);
35319     },
35320     /**
35321     * fetch a  masonry brick based on the masonry brick ID
35322     * @param {string} the masonry brick to add
35323     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35324     */
35325     
35326     get: function(brick_id) 
35327     {
35328         // if (typeof(this.groups[brick_id]) == 'undefined') {
35329         //     return false;
35330         // }
35331         // return this.groups[brick_id] ;
35332         
35333         if(this.groups.key(brick_id)) {
35334             return this.groups.key(brick_id);
35335         }
35336         
35337         return false;
35338     }
35339     
35340     
35341     
35342 });
35343
35344  /*
35345  * - LGPL
35346  *
35347  * element
35348  * 
35349  */
35350
35351 /**
35352  * @class Roo.bootstrap.Brick
35353  * @extends Roo.bootstrap.Component
35354  * Bootstrap Brick class
35355  * 
35356  * @constructor
35357  * Create a new Brick
35358  * @param {Object} config The config object
35359  */
35360
35361 Roo.bootstrap.Brick = function(config){
35362     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35363     
35364     this.addEvents({
35365         // raw events
35366         /**
35367          * @event click
35368          * When a Brick is click
35369          * @param {Roo.bootstrap.Brick} this
35370          * @param {Roo.EventObject} e
35371          */
35372         "click" : true
35373     });
35374 };
35375
35376 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35377     
35378     /**
35379      * @cfg {String} title
35380      */   
35381     title : '',
35382     /**
35383      * @cfg {String} html
35384      */   
35385     html : '',
35386     /**
35387      * @cfg {String} bgimage
35388      */   
35389     bgimage : '',
35390     /**
35391      * @cfg {String} cls
35392      */   
35393     cls : '',
35394     /**
35395      * @cfg {String} href
35396      */   
35397     href : '',
35398     /**
35399      * @cfg {String} video
35400      */   
35401     video : '',
35402     /**
35403      * @cfg {Boolean} square
35404      */   
35405     square : true,
35406     
35407     getAutoCreate : function()
35408     {
35409         var cls = 'roo-brick';
35410         
35411         if(this.href.length){
35412             cls += ' roo-brick-link';
35413         }
35414         
35415         if(this.bgimage.length){
35416             cls += ' roo-brick-image';
35417         }
35418         
35419         if(!this.html.length && !this.bgimage.length){
35420             cls += ' roo-brick-center-title';
35421         }
35422         
35423         if(!this.html.length && this.bgimage.length){
35424             cls += ' roo-brick-bottom-title';
35425         }
35426         
35427         if(this.cls){
35428             cls += ' ' + this.cls;
35429         }
35430         
35431         var cfg = {
35432             tag: (this.href.length) ? 'a' : 'div',
35433             cls: cls,
35434             cn: [
35435                 {
35436                     tag: 'div',
35437                     cls: 'roo-brick-paragraph',
35438                     cn: []
35439                 }
35440             ]
35441         };
35442         
35443         if(this.href.length){
35444             cfg.href = this.href;
35445         }
35446         
35447         var cn = cfg.cn[0].cn;
35448         
35449         if(this.title.length){
35450             cn.push({
35451                 tag: 'h4',
35452                 cls: 'roo-brick-title',
35453                 html: this.title
35454             });
35455         }
35456         
35457         if(this.html.length){
35458             cn.push({
35459                 tag: 'p',
35460                 cls: 'roo-brick-text',
35461                 html: this.html
35462             });
35463         } else {
35464             cn.cls += ' hide';
35465         }
35466         
35467         if(this.bgimage.length){
35468             cfg.cn.push({
35469                 tag: 'img',
35470                 cls: 'roo-brick-image-view',
35471                 src: this.bgimage
35472             });
35473         }
35474         
35475         return cfg;
35476     },
35477     
35478     initEvents: function() 
35479     {
35480         if(this.title.length || this.html.length){
35481             this.el.on('mouseenter'  ,this.enter, this);
35482             this.el.on('mouseleave', this.leave, this);
35483         }
35484         
35485         Roo.EventManager.onWindowResize(this.resize, this); 
35486         
35487         if(this.bgimage.length){
35488             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35489             this.imageEl.on('load', this.onImageLoad, this);
35490             return;
35491         }
35492         
35493         this.resize();
35494     },
35495     
35496     onImageLoad : function()
35497     {
35498         this.resize();
35499     },
35500     
35501     resize : function()
35502     {
35503         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35504         
35505         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35506         
35507         if(this.bgimage.length){
35508             var image = this.el.select('.roo-brick-image-view', true).first();
35509             
35510             image.setWidth(paragraph.getWidth());
35511             
35512             if(this.square){
35513                 image.setHeight(paragraph.getWidth());
35514             }
35515             
35516             this.el.setHeight(image.getHeight());
35517             paragraph.setHeight(image.getHeight());
35518             
35519         }
35520         
35521     },
35522     
35523     enter: function(e, el)
35524     {
35525         e.preventDefault();
35526         
35527         if(this.bgimage.length){
35528             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35529             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35530         }
35531     },
35532     
35533     leave: function(e, el)
35534     {
35535         e.preventDefault();
35536         
35537         if(this.bgimage.length){
35538             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35539             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35540         }
35541     }
35542     
35543 });
35544
35545  
35546
35547  /*
35548  * - LGPL
35549  *
35550  * Number field 
35551  */
35552
35553 /**
35554  * @class Roo.bootstrap.NumberField
35555  * @extends Roo.bootstrap.Input
35556  * Bootstrap NumberField class
35557  * 
35558  * 
35559  * 
35560  * 
35561  * @constructor
35562  * Create a new NumberField
35563  * @param {Object} config The config object
35564  */
35565
35566 Roo.bootstrap.NumberField = function(config){
35567     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35568 };
35569
35570 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35571     
35572     /**
35573      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35574      */
35575     allowDecimals : true,
35576     /**
35577      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35578      */
35579     decimalSeparator : ".",
35580     /**
35581      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35582      */
35583     decimalPrecision : 2,
35584     /**
35585      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35586      */
35587     allowNegative : true,
35588     
35589     /**
35590      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35591      */
35592     allowZero: true,
35593     /**
35594      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35595      */
35596     minValue : Number.NEGATIVE_INFINITY,
35597     /**
35598      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35599      */
35600     maxValue : Number.MAX_VALUE,
35601     /**
35602      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35603      */
35604     minText : "The minimum value for this field is {0}",
35605     /**
35606      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35607      */
35608     maxText : "The maximum value for this field is {0}",
35609     /**
35610      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35611      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35612      */
35613     nanText : "{0} is not a valid number",
35614     /**
35615      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35616      */
35617     thousandsDelimiter : false,
35618     /**
35619      * @cfg {String} valueAlign alignment of value
35620      */
35621     valueAlign : "left",
35622
35623     getAutoCreate : function()
35624     {
35625         var hiddenInput = {
35626             tag: 'input',
35627             type: 'hidden',
35628             id: Roo.id(),
35629             cls: 'hidden-number-input'
35630         };
35631         
35632         if (this.name) {
35633             hiddenInput.name = this.name;
35634         }
35635         
35636         this.name = '';
35637         
35638         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35639         
35640         this.name = hiddenInput.name;
35641         
35642         if(cfg.cn.length > 0) {
35643             cfg.cn.push(hiddenInput);
35644         }
35645         
35646         return cfg;
35647     },
35648
35649     // private
35650     initEvents : function()
35651     {   
35652         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35653         
35654         var allowed = "0123456789";
35655         
35656         if(this.allowDecimals){
35657             allowed += this.decimalSeparator;
35658         }
35659         
35660         if(this.allowNegative){
35661             allowed += "-";
35662         }
35663         
35664         if(this.thousandsDelimiter) {
35665             allowed += ",";
35666         }
35667         
35668         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35669         
35670         var keyPress = function(e){
35671             
35672             var k = e.getKey();
35673             
35674             var c = e.getCharCode();
35675             
35676             if(
35677                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35678                     allowed.indexOf(String.fromCharCode(c)) === -1
35679             ){
35680                 e.stopEvent();
35681                 return;
35682             }
35683             
35684             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35685                 return;
35686             }
35687             
35688             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35689                 e.stopEvent();
35690             }
35691         };
35692         
35693         this.el.on("keypress", keyPress, this);
35694     },
35695     
35696     validateValue : function(value)
35697     {
35698         
35699         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35700             return false;
35701         }
35702         
35703         var num = this.parseValue(value);
35704         
35705         if(isNaN(num)){
35706             this.markInvalid(String.format(this.nanText, value));
35707             return false;
35708         }
35709         
35710         if(num < this.minValue){
35711             this.markInvalid(String.format(this.minText, this.minValue));
35712             return false;
35713         }
35714         
35715         if(num > this.maxValue){
35716             this.markInvalid(String.format(this.maxText, this.maxValue));
35717             return false;
35718         }
35719         
35720         return true;
35721     },
35722
35723     getValue : function()
35724     {
35725         var v = this.hiddenEl().getValue();
35726         
35727         return this.fixPrecision(this.parseValue(v));
35728     },
35729
35730     parseValue : function(value)
35731     {
35732         if(this.thousandsDelimiter) {
35733             value += "";
35734             r = new RegExp(",", "g");
35735             value = value.replace(r, "");
35736         }
35737         
35738         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35739         return isNaN(value) ? '' : value;
35740     },
35741
35742     fixPrecision : function(value)
35743     {
35744         if(this.thousandsDelimiter) {
35745             value += "";
35746             r = new RegExp(",", "g");
35747             value = value.replace(r, "");
35748         }
35749         
35750         var nan = isNaN(value);
35751         
35752         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35753             return nan ? '' : value;
35754         }
35755         return parseFloat(value).toFixed(this.decimalPrecision);
35756     },
35757
35758     setValue : function(v)
35759     {
35760         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35761         
35762         this.value = v;
35763         
35764         if(this.rendered){
35765             
35766             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35767             
35768             this.inputEl().dom.value = (v == '') ? '' :
35769                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35770             
35771             if(!this.allowZero && v === '0') {
35772                 this.hiddenEl().dom.value = '';
35773                 this.inputEl().dom.value = '';
35774             }
35775             
35776             this.validate();
35777         }
35778     },
35779
35780     decimalPrecisionFcn : function(v)
35781     {
35782         return Math.floor(v);
35783     },
35784
35785     beforeBlur : function()
35786     {
35787         var v = this.parseValue(this.getRawValue());
35788         
35789         if(v || v === 0 || v === ''){
35790             this.setValue(v);
35791         }
35792     },
35793     
35794     hiddenEl : function()
35795     {
35796         return this.el.select('input.hidden-number-input',true).first();
35797     }
35798     
35799 });
35800
35801  
35802
35803 /*
35804 * Licence: LGPL
35805 */
35806
35807 /**
35808  * @class Roo.bootstrap.DocumentSlider
35809  * @extends Roo.bootstrap.Component
35810  * Bootstrap DocumentSlider class
35811  * 
35812  * @constructor
35813  * Create a new DocumentViewer
35814  * @param {Object} config The config object
35815  */
35816
35817 Roo.bootstrap.DocumentSlider = function(config){
35818     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35819     
35820     this.files = [];
35821     
35822     this.addEvents({
35823         /**
35824          * @event initial
35825          * Fire after initEvent
35826          * @param {Roo.bootstrap.DocumentSlider} this
35827          */
35828         "initial" : true,
35829         /**
35830          * @event update
35831          * Fire after update
35832          * @param {Roo.bootstrap.DocumentSlider} this
35833          */
35834         "update" : true,
35835         /**
35836          * @event click
35837          * Fire after click
35838          * @param {Roo.bootstrap.DocumentSlider} this
35839          */
35840         "click" : true
35841     });
35842 };
35843
35844 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35845     
35846     files : false,
35847     
35848     indicator : 0,
35849     
35850     getAutoCreate : function()
35851     {
35852         var cfg = {
35853             tag : 'div',
35854             cls : 'roo-document-slider',
35855             cn : [
35856                 {
35857                     tag : 'div',
35858                     cls : 'roo-document-slider-header',
35859                     cn : [
35860                         {
35861                             tag : 'div',
35862                             cls : 'roo-document-slider-header-title'
35863                         }
35864                     ]
35865                 },
35866                 {
35867                     tag : 'div',
35868                     cls : 'roo-document-slider-body',
35869                     cn : [
35870                         {
35871                             tag : 'div',
35872                             cls : 'roo-document-slider-prev',
35873                             cn : [
35874                                 {
35875                                     tag : 'i',
35876                                     cls : 'fa fa-chevron-left'
35877                                 }
35878                             ]
35879                         },
35880                         {
35881                             tag : 'div',
35882                             cls : 'roo-document-slider-thumb',
35883                             cn : [
35884                                 {
35885                                     tag : 'img',
35886                                     cls : 'roo-document-slider-image'
35887                                 }
35888                             ]
35889                         },
35890                         {
35891                             tag : 'div',
35892                             cls : 'roo-document-slider-next',
35893                             cn : [
35894                                 {
35895                                     tag : 'i',
35896                                     cls : 'fa fa-chevron-right'
35897                                 }
35898                             ]
35899                         }
35900                     ]
35901                 }
35902             ]
35903         };
35904         
35905         return cfg;
35906     },
35907     
35908     initEvents : function()
35909     {
35910         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35911         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35912         
35913         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35914         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35915         
35916         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35917         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35918         
35919         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35920         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35921         
35922         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35923         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35924         
35925         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35926         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35927         
35928         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35929         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35930         
35931         this.thumbEl.on('click', this.onClick, this);
35932         
35933         this.prevIndicator.on('click', this.prev, this);
35934         
35935         this.nextIndicator.on('click', this.next, this);
35936         
35937     },
35938     
35939     initial : function()
35940     {
35941         if(this.files.length){
35942             this.indicator = 1;
35943             this.update()
35944         }
35945         
35946         this.fireEvent('initial', this);
35947     },
35948     
35949     update : function()
35950     {
35951         this.imageEl.attr('src', this.files[this.indicator - 1]);
35952         
35953         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35954         
35955         this.prevIndicator.show();
35956         
35957         if(this.indicator == 1){
35958             this.prevIndicator.hide();
35959         }
35960         
35961         this.nextIndicator.show();
35962         
35963         if(this.indicator == this.files.length){
35964             this.nextIndicator.hide();
35965         }
35966         
35967         this.thumbEl.scrollTo('top');
35968         
35969         this.fireEvent('update', this);
35970     },
35971     
35972     onClick : function(e)
35973     {
35974         e.preventDefault();
35975         
35976         this.fireEvent('click', this);
35977     },
35978     
35979     prev : function(e)
35980     {
35981         e.preventDefault();
35982         
35983         this.indicator = Math.max(1, this.indicator - 1);
35984         
35985         this.update();
35986     },
35987     
35988     next : function(e)
35989     {
35990         e.preventDefault();
35991         
35992         this.indicator = Math.min(this.files.length, this.indicator + 1);
35993         
35994         this.update();
35995     }
35996 });
35997 /*
35998  * - LGPL
35999  *
36000  * RadioSet
36001  *
36002  *
36003  */
36004
36005 /**
36006  * @class Roo.bootstrap.RadioSet
36007  * @extends Roo.bootstrap.Input
36008  * Bootstrap RadioSet class
36009  * @cfg {String} indicatorpos (left|right) default left
36010  * @cfg {Boolean} inline (true|false) inline the element (default true)
36011  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36012  * @constructor
36013  * Create a new RadioSet
36014  * @param {Object} config The config object
36015  */
36016
36017 Roo.bootstrap.RadioSet = function(config){
36018     
36019     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36020     
36021     this.radioes = [];
36022     
36023     Roo.bootstrap.RadioSet.register(this);
36024     
36025     this.addEvents({
36026         /**
36027         * @event check
36028         * Fires when the element is checked or unchecked.
36029         * @param {Roo.bootstrap.RadioSet} this This radio
36030         * @param {Roo.bootstrap.Radio} item The checked item
36031         */
36032        check : true,
36033        /**
36034         * @event click
36035         * Fires when the element is click.
36036         * @param {Roo.bootstrap.RadioSet} this This radio set
36037         * @param {Roo.bootstrap.Radio} item The checked item
36038         * @param {Roo.EventObject} e The event object
36039         */
36040        click : true
36041     });
36042     
36043 };
36044
36045 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36046
36047     radioes : false,
36048     
36049     inline : true,
36050     
36051     weight : '',
36052     
36053     indicatorpos : 'left',
36054     
36055     getAutoCreate : function()
36056     {
36057         var label = {
36058             tag : 'label',
36059             cls : 'roo-radio-set-label',
36060             cn : [
36061                 {
36062                     tag : 'span',
36063                     html : this.fieldLabel
36064                 }
36065             ]
36066         };
36067         if (Roo.bootstrap.version == 3) {
36068             
36069             
36070             if(this.indicatorpos == 'left'){
36071                 label.cn.unshift({
36072                     tag : 'i',
36073                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36074                     tooltip : 'This field is required'
36075                 });
36076             } else {
36077                 label.cn.push({
36078                     tag : 'i',
36079                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36080                     tooltip : 'This field is required'
36081                 });
36082             }
36083         }
36084         var items = {
36085             tag : 'div',
36086             cls : 'roo-radio-set-items'
36087         };
36088         
36089         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36090         
36091         if (align === 'left' && this.fieldLabel.length) {
36092             
36093             items = {
36094                 cls : "roo-radio-set-right", 
36095                 cn: [
36096                     items
36097                 ]
36098             };
36099             
36100             if(this.labelWidth > 12){
36101                 label.style = "width: " + this.labelWidth + 'px';
36102             }
36103             
36104             if(this.labelWidth < 13 && this.labelmd == 0){
36105                 this.labelmd = this.labelWidth;
36106             }
36107             
36108             if(this.labellg > 0){
36109                 label.cls += ' col-lg-' + this.labellg;
36110                 items.cls += ' col-lg-' + (12 - this.labellg);
36111             }
36112             
36113             if(this.labelmd > 0){
36114                 label.cls += ' col-md-' + this.labelmd;
36115                 items.cls += ' col-md-' + (12 - this.labelmd);
36116             }
36117             
36118             if(this.labelsm > 0){
36119                 label.cls += ' col-sm-' + this.labelsm;
36120                 items.cls += ' col-sm-' + (12 - this.labelsm);
36121             }
36122             
36123             if(this.labelxs > 0){
36124                 label.cls += ' col-xs-' + this.labelxs;
36125                 items.cls += ' col-xs-' + (12 - this.labelxs);
36126             }
36127         }
36128         
36129         var cfg = {
36130             tag : 'div',
36131             cls : 'roo-radio-set',
36132             cn : [
36133                 {
36134                     tag : 'input',
36135                     cls : 'roo-radio-set-input',
36136                     type : 'hidden',
36137                     name : this.name,
36138                     value : this.value ? this.value :  ''
36139                 },
36140                 label,
36141                 items
36142             ]
36143         };
36144         
36145         if(this.weight.length){
36146             cfg.cls += ' roo-radio-' + this.weight;
36147         }
36148         
36149         if(this.inline) {
36150             cfg.cls += ' roo-radio-set-inline';
36151         }
36152         
36153         var settings=this;
36154         ['xs','sm','md','lg'].map(function(size){
36155             if (settings[size]) {
36156                 cfg.cls += ' col-' + size + '-' + settings[size];
36157             }
36158         });
36159         
36160         return cfg;
36161         
36162     },
36163
36164     initEvents : function()
36165     {
36166         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36167         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36168         
36169         if(!this.fieldLabel.length){
36170             this.labelEl.hide();
36171         }
36172         
36173         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36174         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36175         
36176         this.indicator = this.indicatorEl();
36177         
36178         if(this.indicator){
36179             this.indicator.addClass('invisible');
36180         }
36181         
36182         this.originalValue = this.getValue();
36183         
36184     },
36185     
36186     inputEl: function ()
36187     {
36188         return this.el.select('.roo-radio-set-input', true).first();
36189     },
36190     
36191     getChildContainer : function()
36192     {
36193         return this.itemsEl;
36194     },
36195     
36196     register : function(item)
36197     {
36198         this.radioes.push(item);
36199         
36200     },
36201     
36202     validate : function()
36203     {   
36204         if(this.getVisibilityEl().hasClass('hidden')){
36205             return true;
36206         }
36207         
36208         var valid = false;
36209         
36210         Roo.each(this.radioes, function(i){
36211             if(!i.checked){
36212                 return;
36213             }
36214             
36215             valid = true;
36216             return false;
36217         });
36218         
36219         if(this.allowBlank) {
36220             return true;
36221         }
36222         
36223         if(this.disabled || valid){
36224             this.markValid();
36225             return true;
36226         }
36227         
36228         this.markInvalid();
36229         return false;
36230         
36231     },
36232     
36233     markValid : function()
36234     {
36235         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36236             this.indicatorEl().removeClass('visible');
36237             this.indicatorEl().addClass('invisible');
36238         }
36239         
36240         
36241         if (Roo.bootstrap.version == 3) {
36242             this.el.removeClass([this.invalidClass, this.validClass]);
36243             this.el.addClass(this.validClass);
36244         } else {
36245             this.el.removeClass(['is-invalid','is-valid']);
36246             this.el.addClass(['is-valid']);
36247         }
36248         this.fireEvent('valid', this);
36249     },
36250     
36251     markInvalid : function(msg)
36252     {
36253         if(this.allowBlank || this.disabled){
36254             return;
36255         }
36256         
36257         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36258             this.indicatorEl().removeClass('invisible');
36259             this.indicatorEl().addClass('visible');
36260         }
36261         if (Roo.bootstrap.version == 3) {
36262             this.el.removeClass([this.invalidClass, this.validClass]);
36263             this.el.addClass(this.invalidClass);
36264         } else {
36265             this.el.removeClass(['is-invalid','is-valid']);
36266             this.el.addClass(['is-invalid']);
36267         }
36268         
36269         this.fireEvent('invalid', this, msg);
36270         
36271     },
36272     
36273     setValue : function(v, suppressEvent)
36274     {   
36275         if(this.value === v){
36276             return;
36277         }
36278         
36279         this.value = v;
36280         
36281         if(this.rendered){
36282             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36283         }
36284         
36285         Roo.each(this.radioes, function(i){
36286             i.checked = false;
36287             i.el.removeClass('checked');
36288         });
36289         
36290         Roo.each(this.radioes, function(i){
36291             
36292             if(i.value === v || i.value.toString() === v.toString()){
36293                 i.checked = true;
36294                 i.el.addClass('checked');
36295                 
36296                 if(suppressEvent !== true){
36297                     this.fireEvent('check', this, i);
36298                 }
36299                 
36300                 return false;
36301             }
36302             
36303         }, this);
36304         
36305         this.validate();
36306     },
36307     
36308     clearInvalid : function(){
36309         
36310         if(!this.el || this.preventMark){
36311             return;
36312         }
36313         
36314         this.el.removeClass([this.invalidClass]);
36315         
36316         this.fireEvent('valid', this);
36317     }
36318     
36319 });
36320
36321 Roo.apply(Roo.bootstrap.RadioSet, {
36322     
36323     groups: {},
36324     
36325     register : function(set)
36326     {
36327         this.groups[set.name] = set;
36328     },
36329     
36330     get: function(name) 
36331     {
36332         if (typeof(this.groups[name]) == 'undefined') {
36333             return false;
36334         }
36335         
36336         return this.groups[name] ;
36337     }
36338     
36339 });
36340 /*
36341  * Based on:
36342  * Ext JS Library 1.1.1
36343  * Copyright(c) 2006-2007, Ext JS, LLC.
36344  *
36345  * Originally Released Under LGPL - original licence link has changed is not relivant.
36346  *
36347  * Fork - LGPL
36348  * <script type="text/javascript">
36349  */
36350
36351
36352 /**
36353  * @class Roo.bootstrap.SplitBar
36354  * @extends Roo.util.Observable
36355  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36356  * <br><br>
36357  * Usage:
36358  * <pre><code>
36359 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36360                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36361 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36362 split.minSize = 100;
36363 split.maxSize = 600;
36364 split.animate = true;
36365 split.on('moved', splitterMoved);
36366 </code></pre>
36367  * @constructor
36368  * Create a new SplitBar
36369  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36370  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36371  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36372  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36373                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36374                         position of the SplitBar).
36375  */
36376 Roo.bootstrap.SplitBar = function(cfg){
36377     
36378     /** @private */
36379     
36380     //{
36381     //  dragElement : elm
36382     //  resizingElement: el,
36383         // optional..
36384     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36385     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36386         // existingProxy ???
36387     //}
36388     
36389     this.el = Roo.get(cfg.dragElement, true);
36390     this.el.dom.unselectable = "on";
36391     /** @private */
36392     this.resizingEl = Roo.get(cfg.resizingElement, true);
36393
36394     /**
36395      * @private
36396      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36397      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36398      * @type Number
36399      */
36400     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36401     
36402     /**
36403      * The minimum size of the resizing element. (Defaults to 0)
36404      * @type Number
36405      */
36406     this.minSize = 0;
36407     
36408     /**
36409      * The maximum size of the resizing element. (Defaults to 2000)
36410      * @type Number
36411      */
36412     this.maxSize = 2000;
36413     
36414     /**
36415      * Whether to animate the transition to the new size
36416      * @type Boolean
36417      */
36418     this.animate = false;
36419     
36420     /**
36421      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36422      * @type Boolean
36423      */
36424     this.useShim = false;
36425     
36426     /** @private */
36427     this.shim = null;
36428     
36429     if(!cfg.existingProxy){
36430         /** @private */
36431         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36432     }else{
36433         this.proxy = Roo.get(cfg.existingProxy).dom;
36434     }
36435     /** @private */
36436     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36437     
36438     /** @private */
36439     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36440     
36441     /** @private */
36442     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36443     
36444     /** @private */
36445     this.dragSpecs = {};
36446     
36447     /**
36448      * @private The adapter to use to positon and resize elements
36449      */
36450     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36451     this.adapter.init(this);
36452     
36453     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36454         /** @private */
36455         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36456         this.el.addClass("roo-splitbar-h");
36457     }else{
36458         /** @private */
36459         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36460         this.el.addClass("roo-splitbar-v");
36461     }
36462     
36463     this.addEvents({
36464         /**
36465          * @event resize
36466          * Fires when the splitter is moved (alias for {@link #event-moved})
36467          * @param {Roo.bootstrap.SplitBar} this
36468          * @param {Number} newSize the new width or height
36469          */
36470         "resize" : true,
36471         /**
36472          * @event moved
36473          * Fires when the splitter is moved
36474          * @param {Roo.bootstrap.SplitBar} this
36475          * @param {Number} newSize the new width or height
36476          */
36477         "moved" : true,
36478         /**
36479          * @event beforeresize
36480          * Fires before the splitter is dragged
36481          * @param {Roo.bootstrap.SplitBar} this
36482          */
36483         "beforeresize" : true,
36484
36485         "beforeapply" : true
36486     });
36487
36488     Roo.util.Observable.call(this);
36489 };
36490
36491 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36492     onStartProxyDrag : function(x, y){
36493         this.fireEvent("beforeresize", this);
36494         if(!this.overlay){
36495             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36496             o.unselectable();
36497             o.enableDisplayMode("block");
36498             // all splitbars share the same overlay
36499             Roo.bootstrap.SplitBar.prototype.overlay = o;
36500         }
36501         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36502         this.overlay.show();
36503         Roo.get(this.proxy).setDisplayed("block");
36504         var size = this.adapter.getElementSize(this);
36505         this.activeMinSize = this.getMinimumSize();;
36506         this.activeMaxSize = this.getMaximumSize();;
36507         var c1 = size - this.activeMinSize;
36508         var c2 = Math.max(this.activeMaxSize - size, 0);
36509         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36510             this.dd.resetConstraints();
36511             this.dd.setXConstraint(
36512                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36513                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36514             );
36515             this.dd.setYConstraint(0, 0);
36516         }else{
36517             this.dd.resetConstraints();
36518             this.dd.setXConstraint(0, 0);
36519             this.dd.setYConstraint(
36520                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36521                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36522             );
36523          }
36524         this.dragSpecs.startSize = size;
36525         this.dragSpecs.startPoint = [x, y];
36526         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36527     },
36528     
36529     /** 
36530      * @private Called after the drag operation by the DDProxy
36531      */
36532     onEndProxyDrag : function(e){
36533         Roo.get(this.proxy).setDisplayed(false);
36534         var endPoint = Roo.lib.Event.getXY(e);
36535         if(this.overlay){
36536             this.overlay.hide();
36537         }
36538         var newSize;
36539         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36540             newSize = this.dragSpecs.startSize + 
36541                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36542                     endPoint[0] - this.dragSpecs.startPoint[0] :
36543                     this.dragSpecs.startPoint[0] - endPoint[0]
36544                 );
36545         }else{
36546             newSize = this.dragSpecs.startSize + 
36547                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36548                     endPoint[1] - this.dragSpecs.startPoint[1] :
36549                     this.dragSpecs.startPoint[1] - endPoint[1]
36550                 );
36551         }
36552         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36553         if(newSize != this.dragSpecs.startSize){
36554             if(this.fireEvent('beforeapply', this, newSize) !== false){
36555                 this.adapter.setElementSize(this, newSize);
36556                 this.fireEvent("moved", this, newSize);
36557                 this.fireEvent("resize", this, newSize);
36558             }
36559         }
36560     },
36561     
36562     /**
36563      * Get the adapter this SplitBar uses
36564      * @return The adapter object
36565      */
36566     getAdapter : function(){
36567         return this.adapter;
36568     },
36569     
36570     /**
36571      * Set the adapter this SplitBar uses
36572      * @param {Object} adapter A SplitBar adapter object
36573      */
36574     setAdapter : function(adapter){
36575         this.adapter = adapter;
36576         this.adapter.init(this);
36577     },
36578     
36579     /**
36580      * Gets the minimum size for the resizing element
36581      * @return {Number} The minimum size
36582      */
36583     getMinimumSize : function(){
36584         return this.minSize;
36585     },
36586     
36587     /**
36588      * Sets the minimum size for the resizing element
36589      * @param {Number} minSize The minimum size
36590      */
36591     setMinimumSize : function(minSize){
36592         this.minSize = minSize;
36593     },
36594     
36595     /**
36596      * Gets the maximum size for the resizing element
36597      * @return {Number} The maximum size
36598      */
36599     getMaximumSize : function(){
36600         return this.maxSize;
36601     },
36602     
36603     /**
36604      * Sets the maximum size for the resizing element
36605      * @param {Number} maxSize The maximum size
36606      */
36607     setMaximumSize : function(maxSize){
36608         this.maxSize = maxSize;
36609     },
36610     
36611     /**
36612      * Sets the initialize size for the resizing element
36613      * @param {Number} size The initial size
36614      */
36615     setCurrentSize : function(size){
36616         var oldAnimate = this.animate;
36617         this.animate = false;
36618         this.adapter.setElementSize(this, size);
36619         this.animate = oldAnimate;
36620     },
36621     
36622     /**
36623      * Destroy this splitbar. 
36624      * @param {Boolean} removeEl True to remove the element
36625      */
36626     destroy : function(removeEl){
36627         if(this.shim){
36628             this.shim.remove();
36629         }
36630         this.dd.unreg();
36631         this.proxy.parentNode.removeChild(this.proxy);
36632         if(removeEl){
36633             this.el.remove();
36634         }
36635     }
36636 });
36637
36638 /**
36639  * @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.
36640  */
36641 Roo.bootstrap.SplitBar.createProxy = function(dir){
36642     var proxy = new Roo.Element(document.createElement("div"));
36643     proxy.unselectable();
36644     var cls = 'roo-splitbar-proxy';
36645     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36646     document.body.appendChild(proxy.dom);
36647     return proxy.dom;
36648 };
36649
36650 /** 
36651  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36652  * Default Adapter. It assumes the splitter and resizing element are not positioned
36653  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36654  */
36655 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36656 };
36657
36658 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36659     // do nothing for now
36660     init : function(s){
36661     
36662     },
36663     /**
36664      * Called before drag operations to get the current size of the resizing element. 
36665      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36666      */
36667      getElementSize : function(s){
36668         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36669             return s.resizingEl.getWidth();
36670         }else{
36671             return s.resizingEl.getHeight();
36672         }
36673     },
36674     
36675     /**
36676      * Called after drag operations to set the size of the resizing element.
36677      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36678      * @param {Number} newSize The new size to set
36679      * @param {Function} onComplete A function to be invoked when resizing is complete
36680      */
36681     setElementSize : function(s, newSize, onComplete){
36682         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36683             if(!s.animate){
36684                 s.resizingEl.setWidth(newSize);
36685                 if(onComplete){
36686                     onComplete(s, newSize);
36687                 }
36688             }else{
36689                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36690             }
36691         }else{
36692             
36693             if(!s.animate){
36694                 s.resizingEl.setHeight(newSize);
36695                 if(onComplete){
36696                     onComplete(s, newSize);
36697                 }
36698             }else{
36699                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36700             }
36701         }
36702     }
36703 };
36704
36705 /** 
36706  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36707  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36708  * Adapter that  moves the splitter element to align with the resized sizing element. 
36709  * Used with an absolute positioned SplitBar.
36710  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36711  * document.body, make sure you assign an id to the body element.
36712  */
36713 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36714     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36715     this.container = Roo.get(container);
36716 };
36717
36718 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36719     init : function(s){
36720         this.basic.init(s);
36721     },
36722     
36723     getElementSize : function(s){
36724         return this.basic.getElementSize(s);
36725     },
36726     
36727     setElementSize : function(s, newSize, onComplete){
36728         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36729     },
36730     
36731     moveSplitter : function(s){
36732         var yes = Roo.bootstrap.SplitBar;
36733         switch(s.placement){
36734             case yes.LEFT:
36735                 s.el.setX(s.resizingEl.getRight());
36736                 break;
36737             case yes.RIGHT:
36738                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36739                 break;
36740             case yes.TOP:
36741                 s.el.setY(s.resizingEl.getBottom());
36742                 break;
36743             case yes.BOTTOM:
36744                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36745                 break;
36746         }
36747     }
36748 };
36749
36750 /**
36751  * Orientation constant - Create a vertical SplitBar
36752  * @static
36753  * @type Number
36754  */
36755 Roo.bootstrap.SplitBar.VERTICAL = 1;
36756
36757 /**
36758  * Orientation constant - Create a horizontal SplitBar
36759  * @static
36760  * @type Number
36761  */
36762 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36763
36764 /**
36765  * Placement constant - The resizing element is to the left of the splitter element
36766  * @static
36767  * @type Number
36768  */
36769 Roo.bootstrap.SplitBar.LEFT = 1;
36770
36771 /**
36772  * Placement constant - The resizing element is to the right of the splitter element
36773  * @static
36774  * @type Number
36775  */
36776 Roo.bootstrap.SplitBar.RIGHT = 2;
36777
36778 /**
36779  * Placement constant - The resizing element is positioned above the splitter element
36780  * @static
36781  * @type Number
36782  */
36783 Roo.bootstrap.SplitBar.TOP = 3;
36784
36785 /**
36786  * Placement constant - The resizing element is positioned under splitter element
36787  * @static
36788  * @type Number
36789  */
36790 Roo.bootstrap.SplitBar.BOTTOM = 4;
36791 Roo.namespace("Roo.bootstrap.layout");/*
36792  * Based on:
36793  * Ext JS Library 1.1.1
36794  * Copyright(c) 2006-2007, Ext JS, LLC.
36795  *
36796  * Originally Released Under LGPL - original licence link has changed is not relivant.
36797  *
36798  * Fork - LGPL
36799  * <script type="text/javascript">
36800  */
36801
36802 /**
36803  * @class Roo.bootstrap.layout.Manager
36804  * @extends Roo.bootstrap.Component
36805  * Base class for layout managers.
36806  */
36807 Roo.bootstrap.layout.Manager = function(config)
36808 {
36809     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36810
36811
36812
36813
36814
36815     /** false to disable window resize monitoring @type Boolean */
36816     this.monitorWindowResize = true;
36817     this.regions = {};
36818     this.addEvents({
36819         /**
36820          * @event layout
36821          * Fires when a layout is performed.
36822          * @param {Roo.LayoutManager} this
36823          */
36824         "layout" : true,
36825         /**
36826          * @event regionresized
36827          * Fires when the user resizes a region.
36828          * @param {Roo.LayoutRegion} region The resized region
36829          * @param {Number} newSize The new size (width for east/west, height for north/south)
36830          */
36831         "regionresized" : true,
36832         /**
36833          * @event regioncollapsed
36834          * Fires when a region is collapsed.
36835          * @param {Roo.LayoutRegion} region The collapsed region
36836          */
36837         "regioncollapsed" : true,
36838         /**
36839          * @event regionexpanded
36840          * Fires when a region is expanded.
36841          * @param {Roo.LayoutRegion} region The expanded region
36842          */
36843         "regionexpanded" : true
36844     });
36845     this.updating = false;
36846
36847     if (config.el) {
36848         this.el = Roo.get(config.el);
36849         this.initEvents();
36850     }
36851
36852 };
36853
36854 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36855
36856
36857     regions : null,
36858
36859     monitorWindowResize : true,
36860
36861
36862     updating : false,
36863
36864
36865     onRender : function(ct, position)
36866     {
36867         if(!this.el){
36868             this.el = Roo.get(ct);
36869             this.initEvents();
36870         }
36871         //this.fireEvent('render',this);
36872     },
36873
36874
36875     initEvents: function()
36876     {
36877
36878
36879         // ie scrollbar fix
36880         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36881             document.body.scroll = "no";
36882         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36883             this.el.position('relative');
36884         }
36885         this.id = this.el.id;
36886         this.el.addClass("roo-layout-container");
36887         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36888         if(this.el.dom != document.body ) {
36889             this.el.on('resize', this.layout,this);
36890             this.el.on('show', this.layout,this);
36891         }
36892
36893     },
36894
36895     /**
36896      * Returns true if this layout is currently being updated
36897      * @return {Boolean}
36898      */
36899     isUpdating : function(){
36900         return this.updating;
36901     },
36902
36903     /**
36904      * Suspend the LayoutManager from doing auto-layouts while
36905      * making multiple add or remove calls
36906      */
36907     beginUpdate : function(){
36908         this.updating = true;
36909     },
36910
36911     /**
36912      * Restore auto-layouts and optionally disable the manager from performing a layout
36913      * @param {Boolean} noLayout true to disable a layout update
36914      */
36915     endUpdate : function(noLayout){
36916         this.updating = false;
36917         if(!noLayout){
36918             this.layout();
36919         }
36920     },
36921
36922     layout: function(){
36923         // abstract...
36924     },
36925
36926     onRegionResized : function(region, newSize){
36927         this.fireEvent("regionresized", region, newSize);
36928         this.layout();
36929     },
36930
36931     onRegionCollapsed : function(region){
36932         this.fireEvent("regioncollapsed", region);
36933     },
36934
36935     onRegionExpanded : function(region){
36936         this.fireEvent("regionexpanded", region);
36937     },
36938
36939     /**
36940      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36941      * performs box-model adjustments.
36942      * @return {Object} The size as an object {width: (the width), height: (the height)}
36943      */
36944     getViewSize : function()
36945     {
36946         var size;
36947         if(this.el.dom != document.body){
36948             size = this.el.getSize();
36949         }else{
36950             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36951         }
36952         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36953         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36954         return size;
36955     },
36956
36957     /**
36958      * Returns the Element this layout is bound to.
36959      * @return {Roo.Element}
36960      */
36961     getEl : function(){
36962         return this.el;
36963     },
36964
36965     /**
36966      * Returns the specified region.
36967      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36968      * @return {Roo.LayoutRegion}
36969      */
36970     getRegion : function(target){
36971         return this.regions[target.toLowerCase()];
36972     },
36973
36974     onWindowResize : function(){
36975         if(this.monitorWindowResize){
36976             this.layout();
36977         }
36978     }
36979 });
36980 /*
36981  * Based on:
36982  * Ext JS Library 1.1.1
36983  * Copyright(c) 2006-2007, Ext JS, LLC.
36984  *
36985  * Originally Released Under LGPL - original licence link has changed is not relivant.
36986  *
36987  * Fork - LGPL
36988  * <script type="text/javascript">
36989  */
36990 /**
36991  * @class Roo.bootstrap.layout.Border
36992  * @extends Roo.bootstrap.layout.Manager
36993  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36994  * please see: examples/bootstrap/nested.html<br><br>
36995  
36996 <b>The container the layout is rendered into can be either the body element or any other element.
36997 If it is not the body element, the container needs to either be an absolute positioned element,
36998 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36999 the container size if it is not the body element.</b>
37000
37001 * @constructor
37002 * Create a new Border
37003 * @param {Object} config Configuration options
37004  */
37005 Roo.bootstrap.layout.Border = function(config){
37006     config = config || {};
37007     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37008     
37009     
37010     
37011     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37012         if(config[region]){
37013             config[region].region = region;
37014             this.addRegion(config[region]);
37015         }
37016     },this);
37017     
37018 };
37019
37020 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37021
37022 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37023     
37024     parent : false, // this might point to a 'nest' or a ???
37025     
37026     /**
37027      * Creates and adds a new region if it doesn't already exist.
37028      * @param {String} target The target region key (north, south, east, west or center).
37029      * @param {Object} config The regions config object
37030      * @return {BorderLayoutRegion} The new region
37031      */
37032     addRegion : function(config)
37033     {
37034         if(!this.regions[config.region]){
37035             var r = this.factory(config);
37036             this.bindRegion(r);
37037         }
37038         return this.regions[config.region];
37039     },
37040
37041     // private (kinda)
37042     bindRegion : function(r){
37043         this.regions[r.config.region] = r;
37044         
37045         r.on("visibilitychange",    this.layout, this);
37046         r.on("paneladded",          this.layout, this);
37047         r.on("panelremoved",        this.layout, this);
37048         r.on("invalidated",         this.layout, this);
37049         r.on("resized",             this.onRegionResized, this);
37050         r.on("collapsed",           this.onRegionCollapsed, this);
37051         r.on("expanded",            this.onRegionExpanded, this);
37052     },
37053
37054     /**
37055      * Performs a layout update.
37056      */
37057     layout : function()
37058     {
37059         if(this.updating) {
37060             return;
37061         }
37062         
37063         // render all the rebions if they have not been done alreayd?
37064         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37065             if(this.regions[region] && !this.regions[region].bodyEl){
37066                 this.regions[region].onRender(this.el)
37067             }
37068         },this);
37069         
37070         var size = this.getViewSize();
37071         var w = size.width;
37072         var h = size.height;
37073         var centerW = w;
37074         var centerH = h;
37075         var centerY = 0;
37076         var centerX = 0;
37077         //var x = 0, y = 0;
37078
37079         var rs = this.regions;
37080         var north = rs["north"];
37081         var south = rs["south"]; 
37082         var west = rs["west"];
37083         var east = rs["east"];
37084         var center = rs["center"];
37085         //if(this.hideOnLayout){ // not supported anymore
37086             //c.el.setStyle("display", "none");
37087         //}
37088         if(north && north.isVisible()){
37089             var b = north.getBox();
37090             var m = north.getMargins();
37091             b.width = w - (m.left+m.right);
37092             b.x = m.left;
37093             b.y = m.top;
37094             centerY = b.height + b.y + m.bottom;
37095             centerH -= centerY;
37096             north.updateBox(this.safeBox(b));
37097         }
37098         if(south && south.isVisible()){
37099             var b = south.getBox();
37100             var m = south.getMargins();
37101             b.width = w - (m.left+m.right);
37102             b.x = m.left;
37103             var totalHeight = (b.height + m.top + m.bottom);
37104             b.y = h - totalHeight + m.top;
37105             centerH -= totalHeight;
37106             south.updateBox(this.safeBox(b));
37107         }
37108         if(west && west.isVisible()){
37109             var b = west.getBox();
37110             var m = west.getMargins();
37111             b.height = centerH - (m.top+m.bottom);
37112             b.x = m.left;
37113             b.y = centerY + m.top;
37114             var totalWidth = (b.width + m.left + m.right);
37115             centerX += totalWidth;
37116             centerW -= totalWidth;
37117             west.updateBox(this.safeBox(b));
37118         }
37119         if(east && east.isVisible()){
37120             var b = east.getBox();
37121             var m = east.getMargins();
37122             b.height = centerH - (m.top+m.bottom);
37123             var totalWidth = (b.width + m.left + m.right);
37124             b.x = w - totalWidth + m.left;
37125             b.y = centerY + m.top;
37126             centerW -= totalWidth;
37127             east.updateBox(this.safeBox(b));
37128         }
37129         if(center){
37130             var m = center.getMargins();
37131             var centerBox = {
37132                 x: centerX + m.left,
37133                 y: centerY + m.top,
37134                 width: centerW - (m.left+m.right),
37135                 height: centerH - (m.top+m.bottom)
37136             };
37137             //if(this.hideOnLayout){
37138                 //center.el.setStyle("display", "block");
37139             //}
37140             center.updateBox(this.safeBox(centerBox));
37141         }
37142         this.el.repaint();
37143         this.fireEvent("layout", this);
37144     },
37145
37146     // private
37147     safeBox : function(box){
37148         box.width = Math.max(0, box.width);
37149         box.height = Math.max(0, box.height);
37150         return box;
37151     },
37152
37153     /**
37154      * Adds a ContentPanel (or subclass) to this layout.
37155      * @param {String} target The target region key (north, south, east, west or center).
37156      * @param {Roo.ContentPanel} panel The panel to add
37157      * @return {Roo.ContentPanel} The added panel
37158      */
37159     add : function(target, panel){
37160          
37161         target = target.toLowerCase();
37162         return this.regions[target].add(panel);
37163     },
37164
37165     /**
37166      * Remove a ContentPanel (or subclass) to this layout.
37167      * @param {String} target The target region key (north, south, east, west or center).
37168      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37169      * @return {Roo.ContentPanel} The removed panel
37170      */
37171     remove : function(target, panel){
37172         target = target.toLowerCase();
37173         return this.regions[target].remove(panel);
37174     },
37175
37176     /**
37177      * Searches all regions for a panel with the specified id
37178      * @param {String} panelId
37179      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37180      */
37181     findPanel : function(panelId){
37182         var rs = this.regions;
37183         for(var target in rs){
37184             if(typeof rs[target] != "function"){
37185                 var p = rs[target].getPanel(panelId);
37186                 if(p){
37187                     return p;
37188                 }
37189             }
37190         }
37191         return null;
37192     },
37193
37194     /**
37195      * Searches all regions for a panel with the specified id and activates (shows) it.
37196      * @param {String/ContentPanel} panelId The panels id or the panel itself
37197      * @return {Roo.ContentPanel} The shown panel or null
37198      */
37199     showPanel : function(panelId) {
37200       var rs = this.regions;
37201       for(var target in rs){
37202          var r = rs[target];
37203          if(typeof r != "function"){
37204             if(r.hasPanel(panelId)){
37205                return r.showPanel(panelId);
37206             }
37207          }
37208       }
37209       return null;
37210    },
37211
37212    /**
37213      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37214      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37215      */
37216    /*
37217     restoreState : function(provider){
37218         if(!provider){
37219             provider = Roo.state.Manager;
37220         }
37221         var sm = new Roo.LayoutStateManager();
37222         sm.init(this, provider);
37223     },
37224 */
37225  
37226  
37227     /**
37228      * Adds a xtype elements to the layout.
37229      * <pre><code>
37230
37231 layout.addxtype({
37232        xtype : 'ContentPanel',
37233        region: 'west',
37234        items: [ .... ]
37235    }
37236 );
37237
37238 layout.addxtype({
37239         xtype : 'NestedLayoutPanel',
37240         region: 'west',
37241         layout: {
37242            center: { },
37243            west: { }   
37244         },
37245         items : [ ... list of content panels or nested layout panels.. ]
37246    }
37247 );
37248 </code></pre>
37249      * @param {Object} cfg Xtype definition of item to add.
37250      */
37251     addxtype : function(cfg)
37252     {
37253         // basically accepts a pannel...
37254         // can accept a layout region..!?!?
37255         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37256         
37257         
37258         // theory?  children can only be panels??
37259         
37260         //if (!cfg.xtype.match(/Panel$/)) {
37261         //    return false;
37262         //}
37263         var ret = false;
37264         
37265         if (typeof(cfg.region) == 'undefined') {
37266             Roo.log("Failed to add Panel, region was not set");
37267             Roo.log(cfg);
37268             return false;
37269         }
37270         var region = cfg.region;
37271         delete cfg.region;
37272         
37273           
37274         var xitems = [];
37275         if (cfg.items) {
37276             xitems = cfg.items;
37277             delete cfg.items;
37278         }
37279         var nb = false;
37280         
37281         if ( region == 'center') {
37282             Roo.log("Center: " + cfg.title);
37283         }
37284         
37285         
37286         switch(cfg.xtype) 
37287         {
37288             case 'Content':  // ContentPanel (el, cfg)
37289             case 'Scroll':  // ContentPanel (el, cfg)
37290             case 'View': 
37291                 cfg.autoCreate = cfg.autoCreate || true;
37292                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37293                 //} else {
37294                 //    var el = this.el.createChild();
37295                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37296                 //}
37297                 
37298                 this.add(region, ret);
37299                 break;
37300             
37301             /*
37302             case 'TreePanel': // our new panel!
37303                 cfg.el = this.el.createChild();
37304                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37305                 this.add(region, ret);
37306                 break;
37307             */
37308             
37309             case 'Nest': 
37310                 // create a new Layout (which is  a Border Layout...
37311                 
37312                 var clayout = cfg.layout;
37313                 clayout.el  = this.el.createChild();
37314                 clayout.items   = clayout.items  || [];
37315                 
37316                 delete cfg.layout;
37317                 
37318                 // replace this exitems with the clayout ones..
37319                 xitems = clayout.items;
37320                  
37321                 // force background off if it's in center...
37322                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37323                     cfg.background = false;
37324                 }
37325                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37326                 
37327                 
37328                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37329                 //console.log('adding nested layout panel '  + cfg.toSource());
37330                 this.add(region, ret);
37331                 nb = {}; /// find first...
37332                 break;
37333             
37334             case 'Grid':
37335                 
37336                 // needs grid and region
37337                 
37338                 //var el = this.getRegion(region).el.createChild();
37339                 /*
37340                  *var el = this.el.createChild();
37341                 // create the grid first...
37342                 cfg.grid.container = el;
37343                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37344                 */
37345                 
37346                 if (region == 'center' && this.active ) {
37347                     cfg.background = false;
37348                 }
37349                 
37350                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37351                 
37352                 this.add(region, ret);
37353                 /*
37354                 if (cfg.background) {
37355                     // render grid on panel activation (if panel background)
37356                     ret.on('activate', function(gp) {
37357                         if (!gp.grid.rendered) {
37358                     //        gp.grid.render(el);
37359                         }
37360                     });
37361                 } else {
37362                   //  cfg.grid.render(el);
37363                 }
37364                 */
37365                 break;
37366            
37367            
37368             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37369                 // it was the old xcomponent building that caused this before.
37370                 // espeically if border is the top element in the tree.
37371                 ret = this;
37372                 break; 
37373                 
37374                     
37375                 
37376                 
37377                 
37378             default:
37379                 /*
37380                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37381                     
37382                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37383                     this.add(region, ret);
37384                 } else {
37385                 */
37386                     Roo.log(cfg);
37387                     throw "Can not add '" + cfg.xtype + "' to Border";
37388                     return null;
37389              
37390                                 
37391              
37392         }
37393         this.beginUpdate();
37394         // add children..
37395         var region = '';
37396         var abn = {};
37397         Roo.each(xitems, function(i)  {
37398             region = nb && i.region ? i.region : false;
37399             
37400             var add = ret.addxtype(i);
37401            
37402             if (region) {
37403                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37404                 if (!i.background) {
37405                     abn[region] = nb[region] ;
37406                 }
37407             }
37408             
37409         });
37410         this.endUpdate();
37411
37412         // make the last non-background panel active..
37413         //if (nb) { Roo.log(abn); }
37414         if (nb) {
37415             
37416             for(var r in abn) {
37417                 region = this.getRegion(r);
37418                 if (region) {
37419                     // tried using nb[r], but it does not work..
37420                      
37421                     region.showPanel(abn[r]);
37422                    
37423                 }
37424             }
37425         }
37426         return ret;
37427         
37428     },
37429     
37430     
37431 // private
37432     factory : function(cfg)
37433     {
37434         
37435         var validRegions = Roo.bootstrap.layout.Border.regions;
37436
37437         var target = cfg.region;
37438         cfg.mgr = this;
37439         
37440         var r = Roo.bootstrap.layout;
37441         Roo.log(target);
37442         switch(target){
37443             case "north":
37444                 return new r.North(cfg);
37445             case "south":
37446                 return new r.South(cfg);
37447             case "east":
37448                 return new r.East(cfg);
37449             case "west":
37450                 return new r.West(cfg);
37451             case "center":
37452                 return new r.Center(cfg);
37453         }
37454         throw 'Layout region "'+target+'" not supported.';
37455     }
37456     
37457     
37458 });
37459  /*
37460  * Based on:
37461  * Ext JS Library 1.1.1
37462  * Copyright(c) 2006-2007, Ext JS, LLC.
37463  *
37464  * Originally Released Under LGPL - original licence link has changed is not relivant.
37465  *
37466  * Fork - LGPL
37467  * <script type="text/javascript">
37468  */
37469  
37470 /**
37471  * @class Roo.bootstrap.layout.Basic
37472  * @extends Roo.util.Observable
37473  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37474  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37475  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37476  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37477  * @cfg {string}   region  the region that it inhabits..
37478  * @cfg {bool}   skipConfig skip config?
37479  * 
37480
37481  */
37482 Roo.bootstrap.layout.Basic = function(config){
37483     
37484     this.mgr = config.mgr;
37485     
37486     this.position = config.region;
37487     
37488     var skipConfig = config.skipConfig;
37489     
37490     this.events = {
37491         /**
37492          * @scope Roo.BasicLayoutRegion
37493          */
37494         
37495         /**
37496          * @event beforeremove
37497          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37498          * @param {Roo.LayoutRegion} this
37499          * @param {Roo.ContentPanel} panel The panel
37500          * @param {Object} e The cancel event object
37501          */
37502         "beforeremove" : true,
37503         /**
37504          * @event invalidated
37505          * Fires when the layout for this region is changed.
37506          * @param {Roo.LayoutRegion} this
37507          */
37508         "invalidated" : true,
37509         /**
37510          * @event visibilitychange
37511          * Fires when this region is shown or hidden 
37512          * @param {Roo.LayoutRegion} this
37513          * @param {Boolean} visibility true or false
37514          */
37515         "visibilitychange" : true,
37516         /**
37517          * @event paneladded
37518          * Fires when a panel is added. 
37519          * @param {Roo.LayoutRegion} this
37520          * @param {Roo.ContentPanel} panel The panel
37521          */
37522         "paneladded" : true,
37523         /**
37524          * @event panelremoved
37525          * Fires when a panel is removed. 
37526          * @param {Roo.LayoutRegion} this
37527          * @param {Roo.ContentPanel} panel The panel
37528          */
37529         "panelremoved" : true,
37530         /**
37531          * @event beforecollapse
37532          * Fires when this region before collapse.
37533          * @param {Roo.LayoutRegion} this
37534          */
37535         "beforecollapse" : true,
37536         /**
37537          * @event collapsed
37538          * Fires when this region is collapsed.
37539          * @param {Roo.LayoutRegion} this
37540          */
37541         "collapsed" : true,
37542         /**
37543          * @event expanded
37544          * Fires when this region is expanded.
37545          * @param {Roo.LayoutRegion} this
37546          */
37547         "expanded" : true,
37548         /**
37549          * @event slideshow
37550          * Fires when this region is slid into view.
37551          * @param {Roo.LayoutRegion} this
37552          */
37553         "slideshow" : true,
37554         /**
37555          * @event slidehide
37556          * Fires when this region slides out of view. 
37557          * @param {Roo.LayoutRegion} this
37558          */
37559         "slidehide" : true,
37560         /**
37561          * @event panelactivated
37562          * Fires when a panel is activated. 
37563          * @param {Roo.LayoutRegion} this
37564          * @param {Roo.ContentPanel} panel The activated panel
37565          */
37566         "panelactivated" : true,
37567         /**
37568          * @event resized
37569          * Fires when the user resizes this region. 
37570          * @param {Roo.LayoutRegion} this
37571          * @param {Number} newSize The new size (width for east/west, height for north/south)
37572          */
37573         "resized" : true
37574     };
37575     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37576     this.panels = new Roo.util.MixedCollection();
37577     this.panels.getKey = this.getPanelId.createDelegate(this);
37578     this.box = null;
37579     this.activePanel = null;
37580     // ensure listeners are added...
37581     
37582     if (config.listeners || config.events) {
37583         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37584             listeners : config.listeners || {},
37585             events : config.events || {}
37586         });
37587     }
37588     
37589     if(skipConfig !== true){
37590         this.applyConfig(config);
37591     }
37592 };
37593
37594 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37595 {
37596     getPanelId : function(p){
37597         return p.getId();
37598     },
37599     
37600     applyConfig : function(config){
37601         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37602         this.config = config;
37603         
37604     },
37605     
37606     /**
37607      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37608      * the width, for horizontal (north, south) the height.
37609      * @param {Number} newSize The new width or height
37610      */
37611     resizeTo : function(newSize){
37612         var el = this.el ? this.el :
37613                  (this.activePanel ? this.activePanel.getEl() : null);
37614         if(el){
37615             switch(this.position){
37616                 case "east":
37617                 case "west":
37618                     el.setWidth(newSize);
37619                     this.fireEvent("resized", this, newSize);
37620                 break;
37621                 case "north":
37622                 case "south":
37623                     el.setHeight(newSize);
37624                     this.fireEvent("resized", this, newSize);
37625                 break;                
37626             }
37627         }
37628     },
37629     
37630     getBox : function(){
37631         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37632     },
37633     
37634     getMargins : function(){
37635         return this.margins;
37636     },
37637     
37638     updateBox : function(box){
37639         this.box = box;
37640         var el = this.activePanel.getEl();
37641         el.dom.style.left = box.x + "px";
37642         el.dom.style.top = box.y + "px";
37643         this.activePanel.setSize(box.width, box.height);
37644     },
37645     
37646     /**
37647      * Returns the container element for this region.
37648      * @return {Roo.Element}
37649      */
37650     getEl : function(){
37651         return this.activePanel;
37652     },
37653     
37654     /**
37655      * Returns true if this region is currently visible.
37656      * @return {Boolean}
37657      */
37658     isVisible : function(){
37659         return this.activePanel ? true : false;
37660     },
37661     
37662     setActivePanel : function(panel){
37663         panel = this.getPanel(panel);
37664         if(this.activePanel && this.activePanel != panel){
37665             this.activePanel.setActiveState(false);
37666             this.activePanel.getEl().setLeftTop(-10000,-10000);
37667         }
37668         this.activePanel = panel;
37669         panel.setActiveState(true);
37670         if(this.box){
37671             panel.setSize(this.box.width, this.box.height);
37672         }
37673         this.fireEvent("panelactivated", this, panel);
37674         this.fireEvent("invalidated");
37675     },
37676     
37677     /**
37678      * Show the specified panel.
37679      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37680      * @return {Roo.ContentPanel} The shown panel or null
37681      */
37682     showPanel : function(panel){
37683         panel = this.getPanel(panel);
37684         if(panel){
37685             this.setActivePanel(panel);
37686         }
37687         return panel;
37688     },
37689     
37690     /**
37691      * Get the active panel for this region.
37692      * @return {Roo.ContentPanel} The active panel or null
37693      */
37694     getActivePanel : function(){
37695         return this.activePanel;
37696     },
37697     
37698     /**
37699      * Add the passed ContentPanel(s)
37700      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37701      * @return {Roo.ContentPanel} The panel added (if only one was added)
37702      */
37703     add : function(panel){
37704         if(arguments.length > 1){
37705             for(var i = 0, len = arguments.length; i < len; i++) {
37706                 this.add(arguments[i]);
37707             }
37708             return null;
37709         }
37710         if(this.hasPanel(panel)){
37711             this.showPanel(panel);
37712             return panel;
37713         }
37714         var el = panel.getEl();
37715         if(el.dom.parentNode != this.mgr.el.dom){
37716             this.mgr.el.dom.appendChild(el.dom);
37717         }
37718         if(panel.setRegion){
37719             panel.setRegion(this);
37720         }
37721         this.panels.add(panel);
37722         el.setStyle("position", "absolute");
37723         if(!panel.background){
37724             this.setActivePanel(panel);
37725             if(this.config.initialSize && this.panels.getCount()==1){
37726                 this.resizeTo(this.config.initialSize);
37727             }
37728         }
37729         this.fireEvent("paneladded", this, panel);
37730         return panel;
37731     },
37732     
37733     /**
37734      * Returns true if the panel is in this region.
37735      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37736      * @return {Boolean}
37737      */
37738     hasPanel : function(panel){
37739         if(typeof panel == "object"){ // must be panel obj
37740             panel = panel.getId();
37741         }
37742         return this.getPanel(panel) ? true : false;
37743     },
37744     
37745     /**
37746      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37747      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37748      * @param {Boolean} preservePanel Overrides the config preservePanel option
37749      * @return {Roo.ContentPanel} The panel that was removed
37750      */
37751     remove : function(panel, preservePanel){
37752         panel = this.getPanel(panel);
37753         if(!panel){
37754             return null;
37755         }
37756         var e = {};
37757         this.fireEvent("beforeremove", this, panel, e);
37758         if(e.cancel === true){
37759             return null;
37760         }
37761         var panelId = panel.getId();
37762         this.panels.removeKey(panelId);
37763         return panel;
37764     },
37765     
37766     /**
37767      * Returns the panel specified or null if it's not in this region.
37768      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37769      * @return {Roo.ContentPanel}
37770      */
37771     getPanel : function(id){
37772         if(typeof id == "object"){ // must be panel obj
37773             return id;
37774         }
37775         return this.panels.get(id);
37776     },
37777     
37778     /**
37779      * Returns this regions position (north/south/east/west/center).
37780      * @return {String} 
37781      */
37782     getPosition: function(){
37783         return this.position;    
37784     }
37785 });/*
37786  * Based on:
37787  * Ext JS Library 1.1.1
37788  * Copyright(c) 2006-2007, Ext JS, LLC.
37789  *
37790  * Originally Released Under LGPL - original licence link has changed is not relivant.
37791  *
37792  * Fork - LGPL
37793  * <script type="text/javascript">
37794  */
37795  
37796 /**
37797  * @class Roo.bootstrap.layout.Region
37798  * @extends Roo.bootstrap.layout.Basic
37799  * This class represents a region in a layout manager.
37800  
37801  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37802  * @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})
37803  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37804  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37805  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37806  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37807  * @cfg {String}    title           The title for the region (overrides panel titles)
37808  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37809  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37810  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37811  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37812  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37813  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37814  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37815  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37816  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37817  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37818
37819  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37820  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37821  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37822  * @cfg {Number}    width           For East/West panels
37823  * @cfg {Number}    height          For North/South panels
37824  * @cfg {Boolean}   split           To show the splitter
37825  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37826  * 
37827  * @cfg {string}   cls             Extra CSS classes to add to region
37828  * 
37829  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37830  * @cfg {string}   region  the region that it inhabits..
37831  *
37832
37833  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37834  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37835
37836  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37837  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37838  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37839  */
37840 Roo.bootstrap.layout.Region = function(config)
37841 {
37842     this.applyConfig(config);
37843
37844     var mgr = config.mgr;
37845     var pos = config.region;
37846     config.skipConfig = true;
37847     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37848     
37849     if (mgr.el) {
37850         this.onRender(mgr.el);   
37851     }
37852      
37853     this.visible = true;
37854     this.collapsed = false;
37855     this.unrendered_panels = [];
37856 };
37857
37858 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37859
37860     position: '', // set by wrapper (eg. north/south etc..)
37861     unrendered_panels : null,  // unrendered panels.
37862     
37863     tabPosition : false,
37864     
37865     mgr: false, // points to 'Border'
37866     
37867     
37868     createBody : function(){
37869         /** This region's body element 
37870         * @type Roo.Element */
37871         this.bodyEl = this.el.createChild({
37872                 tag: "div",
37873                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37874         });
37875     },
37876
37877     onRender: function(ctr, pos)
37878     {
37879         var dh = Roo.DomHelper;
37880         /** This region's container element 
37881         * @type Roo.Element */
37882         this.el = dh.append(ctr.dom, {
37883                 tag: "div",
37884                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37885             }, true);
37886         /** This region's title element 
37887         * @type Roo.Element */
37888     
37889         this.titleEl = dh.append(this.el.dom,  {
37890                 tag: "div",
37891                 unselectable: "on",
37892                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37893                 children:[
37894                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37895                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37896                 ]
37897             }, true);
37898         
37899         this.titleEl.enableDisplayMode();
37900         /** This region's title text element 
37901         * @type HTMLElement */
37902         this.titleTextEl = this.titleEl.dom.firstChild;
37903         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37904         /*
37905         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37906         this.closeBtn.enableDisplayMode();
37907         this.closeBtn.on("click", this.closeClicked, this);
37908         this.closeBtn.hide();
37909     */
37910         this.createBody(this.config);
37911         if(this.config.hideWhenEmpty){
37912             this.hide();
37913             this.on("paneladded", this.validateVisibility, this);
37914             this.on("panelremoved", this.validateVisibility, this);
37915         }
37916         if(this.autoScroll){
37917             this.bodyEl.setStyle("overflow", "auto");
37918         }else{
37919             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37920         }
37921         //if(c.titlebar !== false){
37922             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37923                 this.titleEl.hide();
37924             }else{
37925                 this.titleEl.show();
37926                 if(this.config.title){
37927                     this.titleTextEl.innerHTML = this.config.title;
37928                 }
37929             }
37930         //}
37931         if(this.config.collapsed){
37932             this.collapse(true);
37933         }
37934         if(this.config.hidden){
37935             this.hide();
37936         }
37937         
37938         if (this.unrendered_panels && this.unrendered_panels.length) {
37939             for (var i =0;i< this.unrendered_panels.length; i++) {
37940                 this.add(this.unrendered_panels[i]);
37941             }
37942             this.unrendered_panels = null;
37943             
37944         }
37945         
37946     },
37947     
37948     applyConfig : function(c)
37949     {
37950         /*
37951          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37952             var dh = Roo.DomHelper;
37953             if(c.titlebar !== false){
37954                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37955                 this.collapseBtn.on("click", this.collapse, this);
37956                 this.collapseBtn.enableDisplayMode();
37957                 /*
37958                 if(c.showPin === true || this.showPin){
37959                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37960                     this.stickBtn.enableDisplayMode();
37961                     this.stickBtn.on("click", this.expand, this);
37962                     this.stickBtn.hide();
37963                 }
37964                 
37965             }
37966             */
37967             /** This region's collapsed element
37968             * @type Roo.Element */
37969             /*
37970              *
37971             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37972                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37973             ]}, true);
37974             
37975             if(c.floatable !== false){
37976                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37977                this.collapsedEl.on("click", this.collapseClick, this);
37978             }
37979
37980             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37981                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37982                    id: "message", unselectable: "on", style:{"float":"left"}});
37983                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37984              }
37985             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37986             this.expandBtn.on("click", this.expand, this);
37987             
37988         }
37989         
37990         if(this.collapseBtn){
37991             this.collapseBtn.setVisible(c.collapsible == true);
37992         }
37993         
37994         this.cmargins = c.cmargins || this.cmargins ||
37995                          (this.position == "west" || this.position == "east" ?
37996                              {top: 0, left: 2, right:2, bottom: 0} :
37997                              {top: 2, left: 0, right:0, bottom: 2});
37998         */
37999         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38000         
38001         
38002         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38003         
38004         this.autoScroll = c.autoScroll || false;
38005         
38006         
38007        
38008         
38009         this.duration = c.duration || .30;
38010         this.slideDuration = c.slideDuration || .45;
38011         this.config = c;
38012        
38013     },
38014     /**
38015      * Returns true if this region is currently visible.
38016      * @return {Boolean}
38017      */
38018     isVisible : function(){
38019         return this.visible;
38020     },
38021
38022     /**
38023      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38024      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38025      */
38026     //setCollapsedTitle : function(title){
38027     //    title = title || "&#160;";
38028      //   if(this.collapsedTitleTextEl){
38029       //      this.collapsedTitleTextEl.innerHTML = title;
38030        // }
38031     //},
38032
38033     getBox : function(){
38034         var b;
38035       //  if(!this.collapsed){
38036             b = this.el.getBox(false, true);
38037        // }else{
38038           //  b = this.collapsedEl.getBox(false, true);
38039         //}
38040         return b;
38041     },
38042
38043     getMargins : function(){
38044         return this.margins;
38045         //return this.collapsed ? this.cmargins : this.margins;
38046     },
38047 /*
38048     highlight : function(){
38049         this.el.addClass("x-layout-panel-dragover");
38050     },
38051
38052     unhighlight : function(){
38053         this.el.removeClass("x-layout-panel-dragover");
38054     },
38055 */
38056     updateBox : function(box)
38057     {
38058         if (!this.bodyEl) {
38059             return; // not rendered yet..
38060         }
38061         
38062         this.box = box;
38063         if(!this.collapsed){
38064             this.el.dom.style.left = box.x + "px";
38065             this.el.dom.style.top = box.y + "px";
38066             this.updateBody(box.width, box.height);
38067         }else{
38068             this.collapsedEl.dom.style.left = box.x + "px";
38069             this.collapsedEl.dom.style.top = box.y + "px";
38070             this.collapsedEl.setSize(box.width, box.height);
38071         }
38072         if(this.tabs){
38073             this.tabs.autoSizeTabs();
38074         }
38075     },
38076
38077     updateBody : function(w, h)
38078     {
38079         if(w !== null){
38080             this.el.setWidth(w);
38081             w -= this.el.getBorderWidth("rl");
38082             if(this.config.adjustments){
38083                 w += this.config.adjustments[0];
38084             }
38085         }
38086         if(h !== null && h > 0){
38087             this.el.setHeight(h);
38088             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38089             h -= this.el.getBorderWidth("tb");
38090             if(this.config.adjustments){
38091                 h += this.config.adjustments[1];
38092             }
38093             this.bodyEl.setHeight(h);
38094             if(this.tabs){
38095                 h = this.tabs.syncHeight(h);
38096             }
38097         }
38098         if(this.panelSize){
38099             w = w !== null ? w : this.panelSize.width;
38100             h = h !== null ? h : this.panelSize.height;
38101         }
38102         if(this.activePanel){
38103             var el = this.activePanel.getEl();
38104             w = w !== null ? w : el.getWidth();
38105             h = h !== null ? h : el.getHeight();
38106             this.panelSize = {width: w, height: h};
38107             this.activePanel.setSize(w, h);
38108         }
38109         if(Roo.isIE && this.tabs){
38110             this.tabs.el.repaint();
38111         }
38112     },
38113
38114     /**
38115      * Returns the container element for this region.
38116      * @return {Roo.Element}
38117      */
38118     getEl : function(){
38119         return this.el;
38120     },
38121
38122     /**
38123      * Hides this region.
38124      */
38125     hide : function(){
38126         //if(!this.collapsed){
38127             this.el.dom.style.left = "-2000px";
38128             this.el.hide();
38129         //}else{
38130          //   this.collapsedEl.dom.style.left = "-2000px";
38131          //   this.collapsedEl.hide();
38132        // }
38133         this.visible = false;
38134         this.fireEvent("visibilitychange", this, false);
38135     },
38136
38137     /**
38138      * Shows this region if it was previously hidden.
38139      */
38140     show : function(){
38141         //if(!this.collapsed){
38142             this.el.show();
38143         //}else{
38144         //    this.collapsedEl.show();
38145        // }
38146         this.visible = true;
38147         this.fireEvent("visibilitychange", this, true);
38148     },
38149 /*
38150     closeClicked : function(){
38151         if(this.activePanel){
38152             this.remove(this.activePanel);
38153         }
38154     },
38155
38156     collapseClick : function(e){
38157         if(this.isSlid){
38158            e.stopPropagation();
38159            this.slideIn();
38160         }else{
38161            e.stopPropagation();
38162            this.slideOut();
38163         }
38164     },
38165 */
38166     /**
38167      * Collapses this region.
38168      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38169      */
38170     /*
38171     collapse : function(skipAnim, skipCheck = false){
38172         if(this.collapsed) {
38173             return;
38174         }
38175         
38176         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38177             
38178             this.collapsed = true;
38179             if(this.split){
38180                 this.split.el.hide();
38181             }
38182             if(this.config.animate && skipAnim !== true){
38183                 this.fireEvent("invalidated", this);
38184                 this.animateCollapse();
38185             }else{
38186                 this.el.setLocation(-20000,-20000);
38187                 this.el.hide();
38188                 this.collapsedEl.show();
38189                 this.fireEvent("collapsed", this);
38190                 this.fireEvent("invalidated", this);
38191             }
38192         }
38193         
38194     },
38195 */
38196     animateCollapse : function(){
38197         // overridden
38198     },
38199
38200     /**
38201      * Expands this region if it was previously collapsed.
38202      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38203      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38204      */
38205     /*
38206     expand : function(e, skipAnim){
38207         if(e) {
38208             e.stopPropagation();
38209         }
38210         if(!this.collapsed || this.el.hasActiveFx()) {
38211             return;
38212         }
38213         if(this.isSlid){
38214             this.afterSlideIn();
38215             skipAnim = true;
38216         }
38217         this.collapsed = false;
38218         if(this.config.animate && skipAnim !== true){
38219             this.animateExpand();
38220         }else{
38221             this.el.show();
38222             if(this.split){
38223                 this.split.el.show();
38224             }
38225             this.collapsedEl.setLocation(-2000,-2000);
38226             this.collapsedEl.hide();
38227             this.fireEvent("invalidated", this);
38228             this.fireEvent("expanded", this);
38229         }
38230     },
38231 */
38232     animateExpand : function(){
38233         // overridden
38234     },
38235
38236     initTabs : function()
38237     {
38238         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38239         
38240         var ts = new Roo.bootstrap.panel.Tabs({
38241             el: this.bodyEl.dom,
38242             region : this,
38243             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38244             disableTooltips: this.config.disableTabTips,
38245             toolbar : this.config.toolbar
38246         });
38247         
38248         if(this.config.hideTabs){
38249             ts.stripWrap.setDisplayed(false);
38250         }
38251         this.tabs = ts;
38252         ts.resizeTabs = this.config.resizeTabs === true;
38253         ts.minTabWidth = this.config.minTabWidth || 40;
38254         ts.maxTabWidth = this.config.maxTabWidth || 250;
38255         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38256         ts.monitorResize = false;
38257         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38258         ts.bodyEl.addClass('roo-layout-tabs-body');
38259         this.panels.each(this.initPanelAsTab, this);
38260     },
38261
38262     initPanelAsTab : function(panel){
38263         var ti = this.tabs.addTab(
38264             panel.getEl().id,
38265             panel.getTitle(),
38266             null,
38267             this.config.closeOnTab && panel.isClosable(),
38268             panel.tpl
38269         );
38270         if(panel.tabTip !== undefined){
38271             ti.setTooltip(panel.tabTip);
38272         }
38273         ti.on("activate", function(){
38274               this.setActivePanel(panel);
38275         }, this);
38276         
38277         if(this.config.closeOnTab){
38278             ti.on("beforeclose", function(t, e){
38279                 e.cancel = true;
38280                 this.remove(panel);
38281             }, this);
38282         }
38283         
38284         panel.tabItem = ti;
38285         
38286         return ti;
38287     },
38288
38289     updatePanelTitle : function(panel, title)
38290     {
38291         if(this.activePanel == panel){
38292             this.updateTitle(title);
38293         }
38294         if(this.tabs){
38295             var ti = this.tabs.getTab(panel.getEl().id);
38296             ti.setText(title);
38297             if(panel.tabTip !== undefined){
38298                 ti.setTooltip(panel.tabTip);
38299             }
38300         }
38301     },
38302
38303     updateTitle : function(title){
38304         if(this.titleTextEl && !this.config.title){
38305             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38306         }
38307     },
38308
38309     setActivePanel : function(panel)
38310     {
38311         panel = this.getPanel(panel);
38312         if(this.activePanel && this.activePanel != panel){
38313             if(this.activePanel.setActiveState(false) === false){
38314                 return;
38315             }
38316         }
38317         this.activePanel = panel;
38318         panel.setActiveState(true);
38319         if(this.panelSize){
38320             panel.setSize(this.panelSize.width, this.panelSize.height);
38321         }
38322         if(this.closeBtn){
38323             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38324         }
38325         this.updateTitle(panel.getTitle());
38326         if(this.tabs){
38327             this.fireEvent("invalidated", this);
38328         }
38329         this.fireEvent("panelactivated", this, panel);
38330     },
38331
38332     /**
38333      * Shows the specified panel.
38334      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38335      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38336      */
38337     showPanel : function(panel)
38338     {
38339         panel = this.getPanel(panel);
38340         if(panel){
38341             if(this.tabs){
38342                 var tab = this.tabs.getTab(panel.getEl().id);
38343                 if(tab.isHidden()){
38344                     this.tabs.unhideTab(tab.id);
38345                 }
38346                 tab.activate();
38347             }else{
38348                 this.setActivePanel(panel);
38349             }
38350         }
38351         return panel;
38352     },
38353
38354     /**
38355      * Get the active panel for this region.
38356      * @return {Roo.ContentPanel} The active panel or null
38357      */
38358     getActivePanel : function(){
38359         return this.activePanel;
38360     },
38361
38362     validateVisibility : function(){
38363         if(this.panels.getCount() < 1){
38364             this.updateTitle("&#160;");
38365             this.closeBtn.hide();
38366             this.hide();
38367         }else{
38368             if(!this.isVisible()){
38369                 this.show();
38370             }
38371         }
38372     },
38373
38374     /**
38375      * Adds the passed ContentPanel(s) to this region.
38376      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38377      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38378      */
38379     add : function(panel)
38380     {
38381         if(arguments.length > 1){
38382             for(var i = 0, len = arguments.length; i < len; i++) {
38383                 this.add(arguments[i]);
38384             }
38385             return null;
38386         }
38387         
38388         // if we have not been rendered yet, then we can not really do much of this..
38389         if (!this.bodyEl) {
38390             this.unrendered_panels.push(panel);
38391             return panel;
38392         }
38393         
38394         
38395         
38396         
38397         if(this.hasPanel(panel)){
38398             this.showPanel(panel);
38399             return panel;
38400         }
38401         panel.setRegion(this);
38402         this.panels.add(panel);
38403        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38404             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38405             // and hide them... ???
38406             this.bodyEl.dom.appendChild(panel.getEl().dom);
38407             if(panel.background !== true){
38408                 this.setActivePanel(panel);
38409             }
38410             this.fireEvent("paneladded", this, panel);
38411             return panel;
38412         }
38413         */
38414         if(!this.tabs){
38415             this.initTabs();
38416         }else{
38417             this.initPanelAsTab(panel);
38418         }
38419         
38420         
38421         if(panel.background !== true){
38422             this.tabs.activate(panel.getEl().id);
38423         }
38424         this.fireEvent("paneladded", this, panel);
38425         return panel;
38426     },
38427
38428     /**
38429      * Hides the tab for the specified panel.
38430      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38431      */
38432     hidePanel : function(panel){
38433         if(this.tabs && (panel = this.getPanel(panel))){
38434             this.tabs.hideTab(panel.getEl().id);
38435         }
38436     },
38437
38438     /**
38439      * Unhides the tab for a previously hidden panel.
38440      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38441      */
38442     unhidePanel : function(panel){
38443         if(this.tabs && (panel = this.getPanel(panel))){
38444             this.tabs.unhideTab(panel.getEl().id);
38445         }
38446     },
38447
38448     clearPanels : function(){
38449         while(this.panels.getCount() > 0){
38450              this.remove(this.panels.first());
38451         }
38452     },
38453
38454     /**
38455      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38456      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38457      * @param {Boolean} preservePanel Overrides the config preservePanel option
38458      * @return {Roo.ContentPanel} The panel that was removed
38459      */
38460     remove : function(panel, preservePanel)
38461     {
38462         panel = this.getPanel(panel);
38463         if(!panel){
38464             return null;
38465         }
38466         var e = {};
38467         this.fireEvent("beforeremove", this, panel, e);
38468         if(e.cancel === true){
38469             return null;
38470         }
38471         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38472         var panelId = panel.getId();
38473         this.panels.removeKey(panelId);
38474         if(preservePanel){
38475             document.body.appendChild(panel.getEl().dom);
38476         }
38477         if(this.tabs){
38478             this.tabs.removeTab(panel.getEl().id);
38479         }else if (!preservePanel){
38480             this.bodyEl.dom.removeChild(panel.getEl().dom);
38481         }
38482         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38483             var p = this.panels.first();
38484             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38485             tempEl.appendChild(p.getEl().dom);
38486             this.bodyEl.update("");
38487             this.bodyEl.dom.appendChild(p.getEl().dom);
38488             tempEl = null;
38489             this.updateTitle(p.getTitle());
38490             this.tabs = null;
38491             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38492             this.setActivePanel(p);
38493         }
38494         panel.setRegion(null);
38495         if(this.activePanel == panel){
38496             this.activePanel = null;
38497         }
38498         if(this.config.autoDestroy !== false && preservePanel !== true){
38499             try{panel.destroy();}catch(e){}
38500         }
38501         this.fireEvent("panelremoved", this, panel);
38502         return panel;
38503     },
38504
38505     /**
38506      * Returns the TabPanel component used by this region
38507      * @return {Roo.TabPanel}
38508      */
38509     getTabs : function(){
38510         return this.tabs;
38511     },
38512
38513     createTool : function(parentEl, className){
38514         var btn = Roo.DomHelper.append(parentEl, {
38515             tag: "div",
38516             cls: "x-layout-tools-button",
38517             children: [ {
38518                 tag: "div",
38519                 cls: "roo-layout-tools-button-inner " + className,
38520                 html: "&#160;"
38521             }]
38522         }, true);
38523         btn.addClassOnOver("roo-layout-tools-button-over");
38524         return btn;
38525     }
38526 });/*
38527  * Based on:
38528  * Ext JS Library 1.1.1
38529  * Copyright(c) 2006-2007, Ext JS, LLC.
38530  *
38531  * Originally Released Under LGPL - original licence link has changed is not relivant.
38532  *
38533  * Fork - LGPL
38534  * <script type="text/javascript">
38535  */
38536  
38537
38538
38539 /**
38540  * @class Roo.SplitLayoutRegion
38541  * @extends Roo.LayoutRegion
38542  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38543  */
38544 Roo.bootstrap.layout.Split = function(config){
38545     this.cursor = config.cursor;
38546     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38547 };
38548
38549 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38550 {
38551     splitTip : "Drag to resize.",
38552     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38553     useSplitTips : false,
38554
38555     applyConfig : function(config){
38556         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38557     },
38558     
38559     onRender : function(ctr,pos) {
38560         
38561         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38562         if(!this.config.split){
38563             return;
38564         }
38565         if(!this.split){
38566             
38567             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38568                             tag: "div",
38569                             id: this.el.id + "-split",
38570                             cls: "roo-layout-split roo-layout-split-"+this.position,
38571                             html: "&#160;"
38572             });
38573             /** The SplitBar for this region 
38574             * @type Roo.SplitBar */
38575             // does not exist yet...
38576             Roo.log([this.position, this.orientation]);
38577             
38578             this.split = new Roo.bootstrap.SplitBar({
38579                 dragElement : splitEl,
38580                 resizingElement: this.el,
38581                 orientation : this.orientation
38582             });
38583             
38584             this.split.on("moved", this.onSplitMove, this);
38585             this.split.useShim = this.config.useShim === true;
38586             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38587             if(this.useSplitTips){
38588                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38589             }
38590             //if(config.collapsible){
38591             //    this.split.el.on("dblclick", this.collapse,  this);
38592             //}
38593         }
38594         if(typeof this.config.minSize != "undefined"){
38595             this.split.minSize = this.config.minSize;
38596         }
38597         if(typeof this.config.maxSize != "undefined"){
38598             this.split.maxSize = this.config.maxSize;
38599         }
38600         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38601             this.hideSplitter();
38602         }
38603         
38604     },
38605
38606     getHMaxSize : function(){
38607          var cmax = this.config.maxSize || 10000;
38608          var center = this.mgr.getRegion("center");
38609          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38610     },
38611
38612     getVMaxSize : function(){
38613          var cmax = this.config.maxSize || 10000;
38614          var center = this.mgr.getRegion("center");
38615          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38616     },
38617
38618     onSplitMove : function(split, newSize){
38619         this.fireEvent("resized", this, newSize);
38620     },
38621     
38622     /** 
38623      * Returns the {@link Roo.SplitBar} for this region.
38624      * @return {Roo.SplitBar}
38625      */
38626     getSplitBar : function(){
38627         return this.split;
38628     },
38629     
38630     hide : function(){
38631         this.hideSplitter();
38632         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38633     },
38634
38635     hideSplitter : function(){
38636         if(this.split){
38637             this.split.el.setLocation(-2000,-2000);
38638             this.split.el.hide();
38639         }
38640     },
38641
38642     show : function(){
38643         if(this.split){
38644             this.split.el.show();
38645         }
38646         Roo.bootstrap.layout.Split.superclass.show.call(this);
38647     },
38648     
38649     beforeSlide: function(){
38650         if(Roo.isGecko){// firefox overflow auto bug workaround
38651             this.bodyEl.clip();
38652             if(this.tabs) {
38653                 this.tabs.bodyEl.clip();
38654             }
38655             if(this.activePanel){
38656                 this.activePanel.getEl().clip();
38657                 
38658                 if(this.activePanel.beforeSlide){
38659                     this.activePanel.beforeSlide();
38660                 }
38661             }
38662         }
38663     },
38664     
38665     afterSlide : function(){
38666         if(Roo.isGecko){// firefox overflow auto bug workaround
38667             this.bodyEl.unclip();
38668             if(this.tabs) {
38669                 this.tabs.bodyEl.unclip();
38670             }
38671             if(this.activePanel){
38672                 this.activePanel.getEl().unclip();
38673                 if(this.activePanel.afterSlide){
38674                     this.activePanel.afterSlide();
38675                 }
38676             }
38677         }
38678     },
38679
38680     initAutoHide : function(){
38681         if(this.autoHide !== false){
38682             if(!this.autoHideHd){
38683                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38684                 this.autoHideHd = {
38685                     "mouseout": function(e){
38686                         if(!e.within(this.el, true)){
38687                             st.delay(500);
38688                         }
38689                     },
38690                     "mouseover" : function(e){
38691                         st.cancel();
38692                     },
38693                     scope : this
38694                 };
38695             }
38696             this.el.on(this.autoHideHd);
38697         }
38698     },
38699
38700     clearAutoHide : function(){
38701         if(this.autoHide !== false){
38702             this.el.un("mouseout", this.autoHideHd.mouseout);
38703             this.el.un("mouseover", this.autoHideHd.mouseover);
38704         }
38705     },
38706
38707     clearMonitor : function(){
38708         Roo.get(document).un("click", this.slideInIf, this);
38709     },
38710
38711     // these names are backwards but not changed for compat
38712     slideOut : function(){
38713         if(this.isSlid || this.el.hasActiveFx()){
38714             return;
38715         }
38716         this.isSlid = true;
38717         if(this.collapseBtn){
38718             this.collapseBtn.hide();
38719         }
38720         this.closeBtnState = this.closeBtn.getStyle('display');
38721         this.closeBtn.hide();
38722         if(this.stickBtn){
38723             this.stickBtn.show();
38724         }
38725         this.el.show();
38726         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38727         this.beforeSlide();
38728         this.el.setStyle("z-index", 10001);
38729         this.el.slideIn(this.getSlideAnchor(), {
38730             callback: function(){
38731                 this.afterSlide();
38732                 this.initAutoHide();
38733                 Roo.get(document).on("click", this.slideInIf, this);
38734                 this.fireEvent("slideshow", this);
38735             },
38736             scope: this,
38737             block: true
38738         });
38739     },
38740
38741     afterSlideIn : function(){
38742         this.clearAutoHide();
38743         this.isSlid = false;
38744         this.clearMonitor();
38745         this.el.setStyle("z-index", "");
38746         if(this.collapseBtn){
38747             this.collapseBtn.show();
38748         }
38749         this.closeBtn.setStyle('display', this.closeBtnState);
38750         if(this.stickBtn){
38751             this.stickBtn.hide();
38752         }
38753         this.fireEvent("slidehide", this);
38754     },
38755
38756     slideIn : function(cb){
38757         if(!this.isSlid || this.el.hasActiveFx()){
38758             Roo.callback(cb);
38759             return;
38760         }
38761         this.isSlid = false;
38762         this.beforeSlide();
38763         this.el.slideOut(this.getSlideAnchor(), {
38764             callback: function(){
38765                 this.el.setLeftTop(-10000, -10000);
38766                 this.afterSlide();
38767                 this.afterSlideIn();
38768                 Roo.callback(cb);
38769             },
38770             scope: this,
38771             block: true
38772         });
38773     },
38774     
38775     slideInIf : function(e){
38776         if(!e.within(this.el)){
38777             this.slideIn();
38778         }
38779     },
38780
38781     animateCollapse : function(){
38782         this.beforeSlide();
38783         this.el.setStyle("z-index", 20000);
38784         var anchor = this.getSlideAnchor();
38785         this.el.slideOut(anchor, {
38786             callback : function(){
38787                 this.el.setStyle("z-index", "");
38788                 this.collapsedEl.slideIn(anchor, {duration:.3});
38789                 this.afterSlide();
38790                 this.el.setLocation(-10000,-10000);
38791                 this.el.hide();
38792                 this.fireEvent("collapsed", this);
38793             },
38794             scope: this,
38795             block: true
38796         });
38797     },
38798
38799     animateExpand : function(){
38800         this.beforeSlide();
38801         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38802         this.el.setStyle("z-index", 20000);
38803         this.collapsedEl.hide({
38804             duration:.1
38805         });
38806         this.el.slideIn(this.getSlideAnchor(), {
38807             callback : function(){
38808                 this.el.setStyle("z-index", "");
38809                 this.afterSlide();
38810                 if(this.split){
38811                     this.split.el.show();
38812                 }
38813                 this.fireEvent("invalidated", this);
38814                 this.fireEvent("expanded", this);
38815             },
38816             scope: this,
38817             block: true
38818         });
38819     },
38820
38821     anchors : {
38822         "west" : "left",
38823         "east" : "right",
38824         "north" : "top",
38825         "south" : "bottom"
38826     },
38827
38828     sanchors : {
38829         "west" : "l",
38830         "east" : "r",
38831         "north" : "t",
38832         "south" : "b"
38833     },
38834
38835     canchors : {
38836         "west" : "tl-tr",
38837         "east" : "tr-tl",
38838         "north" : "tl-bl",
38839         "south" : "bl-tl"
38840     },
38841
38842     getAnchor : function(){
38843         return this.anchors[this.position];
38844     },
38845
38846     getCollapseAnchor : function(){
38847         return this.canchors[this.position];
38848     },
38849
38850     getSlideAnchor : function(){
38851         return this.sanchors[this.position];
38852     },
38853
38854     getAlignAdj : function(){
38855         var cm = this.cmargins;
38856         switch(this.position){
38857             case "west":
38858                 return [0, 0];
38859             break;
38860             case "east":
38861                 return [0, 0];
38862             break;
38863             case "north":
38864                 return [0, 0];
38865             break;
38866             case "south":
38867                 return [0, 0];
38868             break;
38869         }
38870     },
38871
38872     getExpandAdj : function(){
38873         var c = this.collapsedEl, cm = this.cmargins;
38874         switch(this.position){
38875             case "west":
38876                 return [-(cm.right+c.getWidth()+cm.left), 0];
38877             break;
38878             case "east":
38879                 return [cm.right+c.getWidth()+cm.left, 0];
38880             break;
38881             case "north":
38882                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38883             break;
38884             case "south":
38885                 return [0, cm.top+cm.bottom+c.getHeight()];
38886             break;
38887         }
38888     }
38889 });/*
38890  * Based on:
38891  * Ext JS Library 1.1.1
38892  * Copyright(c) 2006-2007, Ext JS, LLC.
38893  *
38894  * Originally Released Under LGPL - original licence link has changed is not relivant.
38895  *
38896  * Fork - LGPL
38897  * <script type="text/javascript">
38898  */
38899 /*
38900  * These classes are private internal classes
38901  */
38902 Roo.bootstrap.layout.Center = function(config){
38903     config.region = "center";
38904     Roo.bootstrap.layout.Region.call(this, config);
38905     this.visible = true;
38906     this.minWidth = config.minWidth || 20;
38907     this.minHeight = config.minHeight || 20;
38908 };
38909
38910 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38911     hide : function(){
38912         // center panel can't be hidden
38913     },
38914     
38915     show : function(){
38916         // center panel can't be hidden
38917     },
38918     
38919     getMinWidth: function(){
38920         return this.minWidth;
38921     },
38922     
38923     getMinHeight: function(){
38924         return this.minHeight;
38925     }
38926 });
38927
38928
38929
38930
38931  
38932
38933
38934
38935
38936
38937
38938 Roo.bootstrap.layout.North = function(config)
38939 {
38940     config.region = 'north';
38941     config.cursor = 'n-resize';
38942     
38943     Roo.bootstrap.layout.Split.call(this, config);
38944     
38945     
38946     if(this.split){
38947         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38948         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38949         this.split.el.addClass("roo-layout-split-v");
38950     }
38951     var size = config.initialSize || config.height;
38952     if(typeof size != "undefined"){
38953         this.el.setHeight(size);
38954     }
38955 };
38956 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38957 {
38958     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38959     
38960     
38961     
38962     getBox : function(){
38963         if(this.collapsed){
38964             return this.collapsedEl.getBox();
38965         }
38966         var box = this.el.getBox();
38967         if(this.split){
38968             box.height += this.split.el.getHeight();
38969         }
38970         return box;
38971     },
38972     
38973     updateBox : function(box){
38974         if(this.split && !this.collapsed){
38975             box.height -= this.split.el.getHeight();
38976             this.split.el.setLeft(box.x);
38977             this.split.el.setTop(box.y+box.height);
38978             this.split.el.setWidth(box.width);
38979         }
38980         if(this.collapsed){
38981             this.updateBody(box.width, null);
38982         }
38983         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38984     }
38985 });
38986
38987
38988
38989
38990
38991 Roo.bootstrap.layout.South = function(config){
38992     config.region = 'south';
38993     config.cursor = 's-resize';
38994     Roo.bootstrap.layout.Split.call(this, config);
38995     if(this.split){
38996         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38997         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38998         this.split.el.addClass("roo-layout-split-v");
38999     }
39000     var size = config.initialSize || config.height;
39001     if(typeof size != "undefined"){
39002         this.el.setHeight(size);
39003     }
39004 };
39005
39006 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39007     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39008     getBox : function(){
39009         if(this.collapsed){
39010             return this.collapsedEl.getBox();
39011         }
39012         var box = this.el.getBox();
39013         if(this.split){
39014             var sh = this.split.el.getHeight();
39015             box.height += sh;
39016             box.y -= sh;
39017         }
39018         return box;
39019     },
39020     
39021     updateBox : function(box){
39022         if(this.split && !this.collapsed){
39023             var sh = this.split.el.getHeight();
39024             box.height -= sh;
39025             box.y += sh;
39026             this.split.el.setLeft(box.x);
39027             this.split.el.setTop(box.y-sh);
39028             this.split.el.setWidth(box.width);
39029         }
39030         if(this.collapsed){
39031             this.updateBody(box.width, null);
39032         }
39033         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39034     }
39035 });
39036
39037 Roo.bootstrap.layout.East = function(config){
39038     config.region = "east";
39039     config.cursor = "e-resize";
39040     Roo.bootstrap.layout.Split.call(this, config);
39041     if(this.split){
39042         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39043         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39044         this.split.el.addClass("roo-layout-split-h");
39045     }
39046     var size = config.initialSize || config.width;
39047     if(typeof size != "undefined"){
39048         this.el.setWidth(size);
39049     }
39050 };
39051 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39052     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39053     getBox : function(){
39054         if(this.collapsed){
39055             return this.collapsedEl.getBox();
39056         }
39057         var box = this.el.getBox();
39058         if(this.split){
39059             var sw = this.split.el.getWidth();
39060             box.width += sw;
39061             box.x -= sw;
39062         }
39063         return box;
39064     },
39065
39066     updateBox : function(box){
39067         if(this.split && !this.collapsed){
39068             var sw = this.split.el.getWidth();
39069             box.width -= sw;
39070             this.split.el.setLeft(box.x);
39071             this.split.el.setTop(box.y);
39072             this.split.el.setHeight(box.height);
39073             box.x += sw;
39074         }
39075         if(this.collapsed){
39076             this.updateBody(null, box.height);
39077         }
39078         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39079     }
39080 });
39081
39082 Roo.bootstrap.layout.West = function(config){
39083     config.region = "west";
39084     config.cursor = "w-resize";
39085     
39086     Roo.bootstrap.layout.Split.call(this, config);
39087     if(this.split){
39088         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39089         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39090         this.split.el.addClass("roo-layout-split-h");
39091     }
39092     
39093 };
39094 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39095     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39096     
39097     onRender: function(ctr, pos)
39098     {
39099         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39100         var size = this.config.initialSize || this.config.width;
39101         if(typeof size != "undefined"){
39102             this.el.setWidth(size);
39103         }
39104     },
39105     
39106     getBox : function(){
39107         if(this.collapsed){
39108             return this.collapsedEl.getBox();
39109         }
39110         var box = this.el.getBox();
39111         if(this.split){
39112             box.width += this.split.el.getWidth();
39113         }
39114         return box;
39115     },
39116     
39117     updateBox : function(box){
39118         if(this.split && !this.collapsed){
39119             var sw = this.split.el.getWidth();
39120             box.width -= sw;
39121             this.split.el.setLeft(box.x+box.width);
39122             this.split.el.setTop(box.y);
39123             this.split.el.setHeight(box.height);
39124         }
39125         if(this.collapsed){
39126             this.updateBody(null, box.height);
39127         }
39128         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39129     }
39130 });Roo.namespace("Roo.bootstrap.panel");/*
39131  * Based on:
39132  * Ext JS Library 1.1.1
39133  * Copyright(c) 2006-2007, Ext JS, LLC.
39134  *
39135  * Originally Released Under LGPL - original licence link has changed is not relivant.
39136  *
39137  * Fork - LGPL
39138  * <script type="text/javascript">
39139  */
39140 /**
39141  * @class Roo.ContentPanel
39142  * @extends Roo.util.Observable
39143  * A basic ContentPanel element.
39144  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39145  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39146  * @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
39147  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39148  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39149  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39150  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39151  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39152  * @cfg {String} title          The title for this panel
39153  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39154  * @cfg {String} url            Calls {@link #setUrl} with this value
39155  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39156  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39157  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39158  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39159  * @cfg {Boolean} badges render the badges
39160  * @cfg {String} cls  extra classes to use  
39161  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39162
39163  * @constructor
39164  * Create a new ContentPanel.
39165  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39166  * @param {String/Object} config A string to set only the title or a config object
39167  * @param {String} content (optional) Set the HTML content for this panel
39168  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39169  */
39170 Roo.bootstrap.panel.Content = function( config){
39171     
39172     this.tpl = config.tpl || false;
39173     
39174     var el = config.el;
39175     var content = config.content;
39176
39177     if(config.autoCreate){ // xtype is available if this is called from factory
39178         el = Roo.id();
39179     }
39180     this.el = Roo.get(el);
39181     if(!this.el && config && config.autoCreate){
39182         if(typeof config.autoCreate == "object"){
39183             if(!config.autoCreate.id){
39184                 config.autoCreate.id = config.id||el;
39185             }
39186             this.el = Roo.DomHelper.append(document.body,
39187                         config.autoCreate, true);
39188         }else{
39189             var elcfg =  {
39190                 tag: "div",
39191                 cls: (config.cls || '') +
39192                     (config.background ? ' bg-' + config.background : '') +
39193                     " roo-layout-inactive-content",
39194                 id: config.id||el
39195             };
39196             if (config.html) {
39197                 elcfg.html = config.html;
39198                 
39199             }
39200                         
39201             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39202         }
39203     } 
39204     this.closable = false;
39205     this.loaded = false;
39206     this.active = false;
39207    
39208       
39209     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39210         
39211         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39212         
39213         this.wrapEl = this.el; //this.el.wrap();
39214         var ti = [];
39215         if (config.toolbar.items) {
39216             ti = config.toolbar.items ;
39217             delete config.toolbar.items ;
39218         }
39219         
39220         var nitems = [];
39221         this.toolbar.render(this.wrapEl, 'before');
39222         for(var i =0;i < ti.length;i++) {
39223           //  Roo.log(['add child', items[i]]);
39224             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39225         }
39226         this.toolbar.items = nitems;
39227         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39228         delete config.toolbar;
39229         
39230     }
39231     /*
39232     // xtype created footer. - not sure if will work as we normally have to render first..
39233     if (this.footer && !this.footer.el && this.footer.xtype) {
39234         if (!this.wrapEl) {
39235             this.wrapEl = this.el.wrap();
39236         }
39237     
39238         this.footer.container = this.wrapEl.createChild();
39239          
39240         this.footer = Roo.factory(this.footer, Roo);
39241         
39242     }
39243     */
39244     
39245      if(typeof config == "string"){
39246         this.title = config;
39247     }else{
39248         Roo.apply(this, config);
39249     }
39250     
39251     if(this.resizeEl){
39252         this.resizeEl = Roo.get(this.resizeEl, true);
39253     }else{
39254         this.resizeEl = this.el;
39255     }
39256     // handle view.xtype
39257     
39258  
39259     
39260     
39261     this.addEvents({
39262         /**
39263          * @event activate
39264          * Fires when this panel is activated. 
39265          * @param {Roo.ContentPanel} this
39266          */
39267         "activate" : true,
39268         /**
39269          * @event deactivate
39270          * Fires when this panel is activated. 
39271          * @param {Roo.ContentPanel} this
39272          */
39273         "deactivate" : true,
39274
39275         /**
39276          * @event resize
39277          * Fires when this panel is resized if fitToFrame is true.
39278          * @param {Roo.ContentPanel} this
39279          * @param {Number} width The width after any component adjustments
39280          * @param {Number} height The height after any component adjustments
39281          */
39282         "resize" : true,
39283         
39284          /**
39285          * @event render
39286          * Fires when this tab is created
39287          * @param {Roo.ContentPanel} this
39288          */
39289         "render" : true
39290         
39291         
39292         
39293     });
39294     
39295
39296     
39297     
39298     if(this.autoScroll){
39299         this.resizeEl.setStyle("overflow", "auto");
39300     } else {
39301         // fix randome scrolling
39302         //this.el.on('scroll', function() {
39303         //    Roo.log('fix random scolling');
39304         //    this.scrollTo('top',0); 
39305         //});
39306     }
39307     content = content || this.content;
39308     if(content){
39309         this.setContent(content);
39310     }
39311     if(config && config.url){
39312         this.setUrl(this.url, this.params, this.loadOnce);
39313     }
39314     
39315     
39316     
39317     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39318     
39319     if (this.view && typeof(this.view.xtype) != 'undefined') {
39320         this.view.el = this.el.appendChild(document.createElement("div"));
39321         this.view = Roo.factory(this.view); 
39322         this.view.render  &&  this.view.render(false, '');  
39323     }
39324     
39325     
39326     this.fireEvent('render', this);
39327 };
39328
39329 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39330     
39331     cls : '',
39332     background : '',
39333     
39334     tabTip : '',
39335     
39336     setRegion : function(region){
39337         this.region = region;
39338         this.setActiveClass(region && !this.background);
39339     },
39340     
39341     
39342     setActiveClass: function(state)
39343     {
39344         if(state){
39345            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39346            this.el.setStyle('position','relative');
39347         }else{
39348            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39349            this.el.setStyle('position', 'absolute');
39350         } 
39351     },
39352     
39353     /**
39354      * Returns the toolbar for this Panel if one was configured. 
39355      * @return {Roo.Toolbar} 
39356      */
39357     getToolbar : function(){
39358         return this.toolbar;
39359     },
39360     
39361     setActiveState : function(active)
39362     {
39363         this.active = active;
39364         this.setActiveClass(active);
39365         if(!active){
39366             if(this.fireEvent("deactivate", this) === false){
39367                 return false;
39368             }
39369             return true;
39370         }
39371         this.fireEvent("activate", this);
39372         return true;
39373     },
39374     /**
39375      * Updates this panel's element
39376      * @param {String} content The new content
39377      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39378     */
39379     setContent : function(content, loadScripts){
39380         this.el.update(content, loadScripts);
39381     },
39382
39383     ignoreResize : function(w, h){
39384         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39385             return true;
39386         }else{
39387             this.lastSize = {width: w, height: h};
39388             return false;
39389         }
39390     },
39391     /**
39392      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39393      * @return {Roo.UpdateManager} The UpdateManager
39394      */
39395     getUpdateManager : function(){
39396         return this.el.getUpdateManager();
39397     },
39398      /**
39399      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39400      * @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:
39401 <pre><code>
39402 panel.load({
39403     url: "your-url.php",
39404     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39405     callback: yourFunction,
39406     scope: yourObject, //(optional scope)
39407     discardUrl: false,
39408     nocache: false,
39409     text: "Loading...",
39410     timeout: 30,
39411     scripts: false
39412 });
39413 </code></pre>
39414      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39415      * 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.
39416      * @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}
39417      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39418      * @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.
39419      * @return {Roo.ContentPanel} this
39420      */
39421     load : function(){
39422         var um = this.el.getUpdateManager();
39423         um.update.apply(um, arguments);
39424         return this;
39425     },
39426
39427
39428     /**
39429      * 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.
39430      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39431      * @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)
39432      * @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)
39433      * @return {Roo.UpdateManager} The UpdateManager
39434      */
39435     setUrl : function(url, params, loadOnce){
39436         if(this.refreshDelegate){
39437             this.removeListener("activate", this.refreshDelegate);
39438         }
39439         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39440         this.on("activate", this.refreshDelegate);
39441         return this.el.getUpdateManager();
39442     },
39443     
39444     _handleRefresh : function(url, params, loadOnce){
39445         if(!loadOnce || !this.loaded){
39446             var updater = this.el.getUpdateManager();
39447             updater.update(url, params, this._setLoaded.createDelegate(this));
39448         }
39449     },
39450     
39451     _setLoaded : function(){
39452         this.loaded = true;
39453     }, 
39454     
39455     /**
39456      * Returns this panel's id
39457      * @return {String} 
39458      */
39459     getId : function(){
39460         return this.el.id;
39461     },
39462     
39463     /** 
39464      * Returns this panel's element - used by regiosn to add.
39465      * @return {Roo.Element} 
39466      */
39467     getEl : function(){
39468         return this.wrapEl || this.el;
39469     },
39470     
39471    
39472     
39473     adjustForComponents : function(width, height)
39474     {
39475         //Roo.log('adjustForComponents ');
39476         if(this.resizeEl != this.el){
39477             width -= this.el.getFrameWidth('lr');
39478             height -= this.el.getFrameWidth('tb');
39479         }
39480         if(this.toolbar){
39481             var te = this.toolbar.getEl();
39482             te.setWidth(width);
39483             height -= te.getHeight();
39484         }
39485         if(this.footer){
39486             var te = this.footer.getEl();
39487             te.setWidth(width);
39488             height -= te.getHeight();
39489         }
39490         
39491         
39492         if(this.adjustments){
39493             width += this.adjustments[0];
39494             height += this.adjustments[1];
39495         }
39496         return {"width": width, "height": height};
39497     },
39498     
39499     setSize : function(width, height){
39500         if(this.fitToFrame && !this.ignoreResize(width, height)){
39501             if(this.fitContainer && this.resizeEl != this.el){
39502                 this.el.setSize(width, height);
39503             }
39504             var size = this.adjustForComponents(width, height);
39505             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39506             this.fireEvent('resize', this, size.width, size.height);
39507         }
39508     },
39509     
39510     /**
39511      * Returns this panel's title
39512      * @return {String} 
39513      */
39514     getTitle : function(){
39515         
39516         if (typeof(this.title) != 'object') {
39517             return this.title;
39518         }
39519         
39520         var t = '';
39521         for (var k in this.title) {
39522             if (!this.title.hasOwnProperty(k)) {
39523                 continue;
39524             }
39525             
39526             if (k.indexOf('-') >= 0) {
39527                 var s = k.split('-');
39528                 for (var i = 0; i<s.length; i++) {
39529                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39530                 }
39531             } else {
39532                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39533             }
39534         }
39535         return t;
39536     },
39537     
39538     /**
39539      * Set this panel's title
39540      * @param {String} title
39541      */
39542     setTitle : function(title){
39543         this.title = title;
39544         if(this.region){
39545             this.region.updatePanelTitle(this, title);
39546         }
39547     },
39548     
39549     /**
39550      * Returns true is this panel was configured to be closable
39551      * @return {Boolean} 
39552      */
39553     isClosable : function(){
39554         return this.closable;
39555     },
39556     
39557     beforeSlide : function(){
39558         this.el.clip();
39559         this.resizeEl.clip();
39560     },
39561     
39562     afterSlide : function(){
39563         this.el.unclip();
39564         this.resizeEl.unclip();
39565     },
39566     
39567     /**
39568      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39569      *   Will fail silently if the {@link #setUrl} method has not been called.
39570      *   This does not activate the panel, just updates its content.
39571      */
39572     refresh : function(){
39573         if(this.refreshDelegate){
39574            this.loaded = false;
39575            this.refreshDelegate();
39576         }
39577     },
39578     
39579     /**
39580      * Destroys this panel
39581      */
39582     destroy : function(){
39583         this.el.removeAllListeners();
39584         var tempEl = document.createElement("span");
39585         tempEl.appendChild(this.el.dom);
39586         tempEl.innerHTML = "";
39587         this.el.remove();
39588         this.el = null;
39589     },
39590     
39591     /**
39592      * form - if the content panel contains a form - this is a reference to it.
39593      * @type {Roo.form.Form}
39594      */
39595     form : false,
39596     /**
39597      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39598      *    This contains a reference to it.
39599      * @type {Roo.View}
39600      */
39601     view : false,
39602     
39603       /**
39604      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39605      * <pre><code>
39606
39607 layout.addxtype({
39608        xtype : 'Form',
39609        items: [ .... ]
39610    }
39611 );
39612
39613 </code></pre>
39614      * @param {Object} cfg Xtype definition of item to add.
39615      */
39616     
39617     
39618     getChildContainer: function () {
39619         return this.getEl();
39620     }
39621     
39622     
39623     /*
39624         var  ret = new Roo.factory(cfg);
39625         return ret;
39626         
39627         
39628         // add form..
39629         if (cfg.xtype.match(/^Form$/)) {
39630             
39631             var el;
39632             //if (this.footer) {
39633             //    el = this.footer.container.insertSibling(false, 'before');
39634             //} else {
39635                 el = this.el.createChild();
39636             //}
39637
39638             this.form = new  Roo.form.Form(cfg);
39639             
39640             
39641             if ( this.form.allItems.length) {
39642                 this.form.render(el.dom);
39643             }
39644             return this.form;
39645         }
39646         // should only have one of theses..
39647         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39648             // views.. should not be just added - used named prop 'view''
39649             
39650             cfg.el = this.el.appendChild(document.createElement("div"));
39651             // factory?
39652             
39653             var ret = new Roo.factory(cfg);
39654              
39655              ret.render && ret.render(false, ''); // render blank..
39656             this.view = ret;
39657             return ret;
39658         }
39659         return false;
39660     }
39661     \*/
39662 });
39663  
39664 /**
39665  * @class Roo.bootstrap.panel.Grid
39666  * @extends Roo.bootstrap.panel.Content
39667  * @constructor
39668  * Create a new GridPanel.
39669  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39670  * @param {Object} config A the config object
39671   
39672  */
39673
39674
39675
39676 Roo.bootstrap.panel.Grid = function(config)
39677 {
39678     
39679       
39680     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39681         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39682
39683     config.el = this.wrapper;
39684     //this.el = this.wrapper;
39685     
39686       if (config.container) {
39687         // ctor'ed from a Border/panel.grid
39688         
39689         
39690         this.wrapper.setStyle("overflow", "hidden");
39691         this.wrapper.addClass('roo-grid-container');
39692
39693     }
39694     
39695     
39696     if(config.toolbar){
39697         var tool_el = this.wrapper.createChild();    
39698         this.toolbar = Roo.factory(config.toolbar);
39699         var ti = [];
39700         if (config.toolbar.items) {
39701             ti = config.toolbar.items ;
39702             delete config.toolbar.items ;
39703         }
39704         
39705         var nitems = [];
39706         this.toolbar.render(tool_el);
39707         for(var i =0;i < ti.length;i++) {
39708           //  Roo.log(['add child', items[i]]);
39709             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39710         }
39711         this.toolbar.items = nitems;
39712         
39713         delete config.toolbar;
39714     }
39715     
39716     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39717     config.grid.scrollBody = true;;
39718     config.grid.monitorWindowResize = false; // turn off autosizing
39719     config.grid.autoHeight = false;
39720     config.grid.autoWidth = false;
39721     
39722     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39723     
39724     if (config.background) {
39725         // render grid on panel activation (if panel background)
39726         this.on('activate', function(gp) {
39727             if (!gp.grid.rendered) {
39728                 gp.grid.render(this.wrapper);
39729                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39730             }
39731         });
39732             
39733     } else {
39734         this.grid.render(this.wrapper);
39735         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39736
39737     }
39738     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39739     // ??? needed ??? config.el = this.wrapper;
39740     
39741     
39742     
39743   
39744     // xtype created footer. - not sure if will work as we normally have to render first..
39745     if (this.footer && !this.footer.el && this.footer.xtype) {
39746         
39747         var ctr = this.grid.getView().getFooterPanel(true);
39748         this.footer.dataSource = this.grid.dataSource;
39749         this.footer = Roo.factory(this.footer, Roo);
39750         this.footer.render(ctr);
39751         
39752     }
39753     
39754     
39755     
39756     
39757      
39758 };
39759
39760 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39761     getId : function(){
39762         return this.grid.id;
39763     },
39764     
39765     /**
39766      * Returns the grid for this panel
39767      * @return {Roo.bootstrap.Table} 
39768      */
39769     getGrid : function(){
39770         return this.grid;    
39771     },
39772     
39773     setSize : function(width, height){
39774         if(!this.ignoreResize(width, height)){
39775             var grid = this.grid;
39776             var size = this.adjustForComponents(width, height);
39777             // tfoot is not a footer?
39778           
39779             
39780             var gridel = grid.getGridEl();
39781             gridel.setSize(size.width, size.height);
39782             
39783             var tbd = grid.getGridEl().select('tbody', true).first();
39784             var thd = grid.getGridEl().select('thead',true).first();
39785             var tbf= grid.getGridEl().select('tfoot', true).first();
39786
39787             if (tbf) {
39788                 size.height -= thd.getHeight();
39789             }
39790             if (thd) {
39791                 size.height -= thd.getHeight();
39792             }
39793             
39794             tbd.setSize(size.width, size.height );
39795             // this is for the account management tab -seems to work there.
39796             var thd = grid.getGridEl().select('thead',true).first();
39797             //if (tbd) {
39798             //    tbd.setSize(size.width, size.height - thd.getHeight());
39799             //}
39800              
39801             grid.autoSize();
39802         }
39803     },
39804      
39805     
39806     
39807     beforeSlide : function(){
39808         this.grid.getView().scroller.clip();
39809     },
39810     
39811     afterSlide : function(){
39812         this.grid.getView().scroller.unclip();
39813     },
39814     
39815     destroy : function(){
39816         this.grid.destroy();
39817         delete this.grid;
39818         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39819     }
39820 });
39821
39822 /**
39823  * @class Roo.bootstrap.panel.Nest
39824  * @extends Roo.bootstrap.panel.Content
39825  * @constructor
39826  * Create a new Panel, that can contain a layout.Border.
39827  * 
39828  * 
39829  * @param {Roo.BorderLayout} layout The layout for this panel
39830  * @param {String/Object} config A string to set only the title or a config object
39831  */
39832 Roo.bootstrap.panel.Nest = function(config)
39833 {
39834     // construct with only one argument..
39835     /* FIXME - implement nicer consturctors
39836     if (layout.layout) {
39837         config = layout;
39838         layout = config.layout;
39839         delete config.layout;
39840     }
39841     if (layout.xtype && !layout.getEl) {
39842         // then layout needs constructing..
39843         layout = Roo.factory(layout, Roo);
39844     }
39845     */
39846     
39847     config.el =  config.layout.getEl();
39848     
39849     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39850     
39851     config.layout.monitorWindowResize = false; // turn off autosizing
39852     this.layout = config.layout;
39853     this.layout.getEl().addClass("roo-layout-nested-layout");
39854     this.layout.parent = this;
39855     
39856     
39857     
39858     
39859 };
39860
39861 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39862
39863     setSize : function(width, height){
39864         if(!this.ignoreResize(width, height)){
39865             var size = this.adjustForComponents(width, height);
39866             var el = this.layout.getEl();
39867             if (size.height < 1) {
39868                 el.setWidth(size.width);   
39869             } else {
39870                 el.setSize(size.width, size.height);
39871             }
39872             var touch = el.dom.offsetWidth;
39873             this.layout.layout();
39874             // ie requires a double layout on the first pass
39875             if(Roo.isIE && !this.initialized){
39876                 this.initialized = true;
39877                 this.layout.layout();
39878             }
39879         }
39880     },
39881     
39882     // activate all subpanels if not currently active..
39883     
39884     setActiveState : function(active){
39885         this.active = active;
39886         this.setActiveClass(active);
39887         
39888         if(!active){
39889             this.fireEvent("deactivate", this);
39890             return;
39891         }
39892         
39893         this.fireEvent("activate", this);
39894         // not sure if this should happen before or after..
39895         if (!this.layout) {
39896             return; // should not happen..
39897         }
39898         var reg = false;
39899         for (var r in this.layout.regions) {
39900             reg = this.layout.getRegion(r);
39901             if (reg.getActivePanel()) {
39902                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39903                 reg.setActivePanel(reg.getActivePanel());
39904                 continue;
39905             }
39906             if (!reg.panels.length) {
39907                 continue;
39908             }
39909             reg.showPanel(reg.getPanel(0));
39910         }
39911         
39912         
39913         
39914         
39915     },
39916     
39917     /**
39918      * Returns the nested BorderLayout for this panel
39919      * @return {Roo.BorderLayout} 
39920      */
39921     getLayout : function(){
39922         return this.layout;
39923     },
39924     
39925      /**
39926      * Adds a xtype elements to the layout of the nested panel
39927      * <pre><code>
39928
39929 panel.addxtype({
39930        xtype : 'ContentPanel',
39931        region: 'west',
39932        items: [ .... ]
39933    }
39934 );
39935
39936 panel.addxtype({
39937         xtype : 'NestedLayoutPanel',
39938         region: 'west',
39939         layout: {
39940            center: { },
39941            west: { }   
39942         },
39943         items : [ ... list of content panels or nested layout panels.. ]
39944    }
39945 );
39946 </code></pre>
39947      * @param {Object} cfg Xtype definition of item to add.
39948      */
39949     addxtype : function(cfg) {
39950         return this.layout.addxtype(cfg);
39951     
39952     }
39953 });/*
39954  * Based on:
39955  * Ext JS Library 1.1.1
39956  * Copyright(c) 2006-2007, Ext JS, LLC.
39957  *
39958  * Originally Released Under LGPL - original licence link has changed is not relivant.
39959  *
39960  * Fork - LGPL
39961  * <script type="text/javascript">
39962  */
39963 /**
39964  * @class Roo.TabPanel
39965  * @extends Roo.util.Observable
39966  * A lightweight tab container.
39967  * <br><br>
39968  * Usage:
39969  * <pre><code>
39970 // basic tabs 1, built from existing content
39971 var tabs = new Roo.TabPanel("tabs1");
39972 tabs.addTab("script", "View Script");
39973 tabs.addTab("markup", "View Markup");
39974 tabs.activate("script");
39975
39976 // more advanced tabs, built from javascript
39977 var jtabs = new Roo.TabPanel("jtabs");
39978 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39979
39980 // set up the UpdateManager
39981 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39982 var updater = tab2.getUpdateManager();
39983 updater.setDefaultUrl("ajax1.htm");
39984 tab2.on('activate', updater.refresh, updater, true);
39985
39986 // Use setUrl for Ajax loading
39987 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39988 tab3.setUrl("ajax2.htm", null, true);
39989
39990 // Disabled tab
39991 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39992 tab4.disable();
39993
39994 jtabs.activate("jtabs-1");
39995  * </code></pre>
39996  * @constructor
39997  * Create a new TabPanel.
39998  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39999  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40000  */
40001 Roo.bootstrap.panel.Tabs = function(config){
40002     /**
40003     * The container element for this TabPanel.
40004     * @type Roo.Element
40005     */
40006     this.el = Roo.get(config.el);
40007     delete config.el;
40008     if(config){
40009         if(typeof config == "boolean"){
40010             this.tabPosition = config ? "bottom" : "top";
40011         }else{
40012             Roo.apply(this, config);
40013         }
40014     }
40015     
40016     if(this.tabPosition == "bottom"){
40017         // if tabs are at the bottom = create the body first.
40018         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40019         this.el.addClass("roo-tabs-bottom");
40020     }
40021     // next create the tabs holders
40022     
40023     if (this.tabPosition == "west"){
40024         
40025         var reg = this.region; // fake it..
40026         while (reg) {
40027             if (!reg.mgr.parent) {
40028                 break;
40029             }
40030             reg = reg.mgr.parent.region;
40031         }
40032         Roo.log("got nest?");
40033         Roo.log(reg);
40034         if (reg.mgr.getRegion('west')) {
40035             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40036             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40037             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40038             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40039             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40040         
40041             
40042         }
40043         
40044         
40045     } else {
40046      
40047         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40048         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40049         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40050         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40051     }
40052     
40053     
40054     if(Roo.isIE){
40055         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40056     }
40057     
40058     // finally - if tabs are at the top, then create the body last..
40059     if(this.tabPosition != "bottom"){
40060         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40061          * @type Roo.Element
40062          */
40063         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40064         this.el.addClass("roo-tabs-top");
40065     }
40066     this.items = [];
40067
40068     this.bodyEl.setStyle("position", "relative");
40069
40070     this.active = null;
40071     this.activateDelegate = this.activate.createDelegate(this);
40072
40073     this.addEvents({
40074         /**
40075          * @event tabchange
40076          * Fires when the active tab changes
40077          * @param {Roo.TabPanel} this
40078          * @param {Roo.TabPanelItem} activePanel The new active tab
40079          */
40080         "tabchange": true,
40081         /**
40082          * @event beforetabchange
40083          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40084          * @param {Roo.TabPanel} this
40085          * @param {Object} e Set cancel to true on this object to cancel the tab change
40086          * @param {Roo.TabPanelItem} tab The tab being changed to
40087          */
40088         "beforetabchange" : true
40089     });
40090
40091     Roo.EventManager.onWindowResize(this.onResize, this);
40092     this.cpad = this.el.getPadding("lr");
40093     this.hiddenCount = 0;
40094
40095
40096     // toolbar on the tabbar support...
40097     if (this.toolbar) {
40098         alert("no toolbar support yet");
40099         this.toolbar  = false;
40100         /*
40101         var tcfg = this.toolbar;
40102         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40103         this.toolbar = new Roo.Toolbar(tcfg);
40104         if (Roo.isSafari) {
40105             var tbl = tcfg.container.child('table', true);
40106             tbl.setAttribute('width', '100%');
40107         }
40108         */
40109         
40110     }
40111    
40112
40113
40114     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40115 };
40116
40117 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40118     /*
40119      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40120      */
40121     tabPosition : "top",
40122     /*
40123      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40124      */
40125     currentTabWidth : 0,
40126     /*
40127      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40128      */
40129     minTabWidth : 40,
40130     /*
40131      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40132      */
40133     maxTabWidth : 250,
40134     /*
40135      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40136      */
40137     preferredTabWidth : 175,
40138     /*
40139      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40140      */
40141     resizeTabs : false,
40142     /*
40143      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40144      */
40145     monitorResize : true,
40146     /*
40147      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40148      */
40149     toolbar : false,  // set by caller..
40150     
40151     region : false, /// set by caller
40152     
40153     disableTooltips : true, // not used yet...
40154
40155     /**
40156      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40157      * @param {String} id The id of the div to use <b>or create</b>
40158      * @param {String} text The text for the tab
40159      * @param {String} content (optional) Content to put in the TabPanelItem body
40160      * @param {Boolean} closable (optional) True to create a close icon on the tab
40161      * @return {Roo.TabPanelItem} The created TabPanelItem
40162      */
40163     addTab : function(id, text, content, closable, tpl)
40164     {
40165         var item = new Roo.bootstrap.panel.TabItem({
40166             panel: this,
40167             id : id,
40168             text : text,
40169             closable : closable,
40170             tpl : tpl
40171         });
40172         this.addTabItem(item);
40173         if(content){
40174             item.setContent(content);
40175         }
40176         return item;
40177     },
40178
40179     /**
40180      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40181      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40182      * @return {Roo.TabPanelItem}
40183      */
40184     getTab : function(id){
40185         return this.items[id];
40186     },
40187
40188     /**
40189      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40190      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40191      */
40192     hideTab : function(id){
40193         var t = this.items[id];
40194         if(!t.isHidden()){
40195            t.setHidden(true);
40196            this.hiddenCount++;
40197            this.autoSizeTabs();
40198         }
40199     },
40200
40201     /**
40202      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40203      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40204      */
40205     unhideTab : function(id){
40206         var t = this.items[id];
40207         if(t.isHidden()){
40208            t.setHidden(false);
40209            this.hiddenCount--;
40210            this.autoSizeTabs();
40211         }
40212     },
40213
40214     /**
40215      * Adds an existing {@link Roo.TabPanelItem}.
40216      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40217      */
40218     addTabItem : function(item)
40219     {
40220         this.items[item.id] = item;
40221         this.items.push(item);
40222         this.autoSizeTabs();
40223       //  if(this.resizeTabs){
40224     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40225   //         this.autoSizeTabs();
40226 //        }else{
40227 //            item.autoSize();
40228        // }
40229     },
40230
40231     /**
40232      * Removes a {@link Roo.TabPanelItem}.
40233      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40234      */
40235     removeTab : function(id){
40236         var items = this.items;
40237         var tab = items[id];
40238         if(!tab) { return; }
40239         var index = items.indexOf(tab);
40240         if(this.active == tab && items.length > 1){
40241             var newTab = this.getNextAvailable(index);
40242             if(newTab) {
40243                 newTab.activate();
40244             }
40245         }
40246         this.stripEl.dom.removeChild(tab.pnode.dom);
40247         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40248             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40249         }
40250         items.splice(index, 1);
40251         delete this.items[tab.id];
40252         tab.fireEvent("close", tab);
40253         tab.purgeListeners();
40254         this.autoSizeTabs();
40255     },
40256
40257     getNextAvailable : function(start){
40258         var items = this.items;
40259         var index = start;
40260         // look for a next tab that will slide over to
40261         // replace the one being removed
40262         while(index < items.length){
40263             var item = items[++index];
40264             if(item && !item.isHidden()){
40265                 return item;
40266             }
40267         }
40268         // if one isn't found select the previous tab (on the left)
40269         index = start;
40270         while(index >= 0){
40271             var item = items[--index];
40272             if(item && !item.isHidden()){
40273                 return item;
40274             }
40275         }
40276         return null;
40277     },
40278
40279     /**
40280      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40281      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40282      */
40283     disableTab : function(id){
40284         var tab = this.items[id];
40285         if(tab && this.active != tab){
40286             tab.disable();
40287         }
40288     },
40289
40290     /**
40291      * Enables a {@link Roo.TabPanelItem} that is disabled.
40292      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40293      */
40294     enableTab : function(id){
40295         var tab = this.items[id];
40296         tab.enable();
40297     },
40298
40299     /**
40300      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40301      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40302      * @return {Roo.TabPanelItem} The TabPanelItem.
40303      */
40304     activate : function(id)
40305     {
40306         //Roo.log('activite:'  + id);
40307         
40308         var tab = this.items[id];
40309         if(!tab){
40310             return null;
40311         }
40312         if(tab == this.active || tab.disabled){
40313             return tab;
40314         }
40315         var e = {};
40316         this.fireEvent("beforetabchange", this, e, tab);
40317         if(e.cancel !== true && !tab.disabled){
40318             if(this.active){
40319                 this.active.hide();
40320             }
40321             this.active = this.items[id];
40322             this.active.show();
40323             this.fireEvent("tabchange", this, this.active);
40324         }
40325         return tab;
40326     },
40327
40328     /**
40329      * Gets the active {@link Roo.TabPanelItem}.
40330      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40331      */
40332     getActiveTab : function(){
40333         return this.active;
40334     },
40335
40336     /**
40337      * Updates the tab body element to fit the height of the container element
40338      * for overflow scrolling
40339      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40340      */
40341     syncHeight : function(targetHeight){
40342         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40343         var bm = this.bodyEl.getMargins();
40344         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40345         this.bodyEl.setHeight(newHeight);
40346         return newHeight;
40347     },
40348
40349     onResize : function(){
40350         if(this.monitorResize){
40351             this.autoSizeTabs();
40352         }
40353     },
40354
40355     /**
40356      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40357      */
40358     beginUpdate : function(){
40359         this.updating = true;
40360     },
40361
40362     /**
40363      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40364      */
40365     endUpdate : function(){
40366         this.updating = false;
40367         this.autoSizeTabs();
40368     },
40369
40370     /**
40371      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40372      */
40373     autoSizeTabs : function()
40374     {
40375         var count = this.items.length;
40376         var vcount = count - this.hiddenCount;
40377         
40378         if (vcount < 2) {
40379             this.stripEl.hide();
40380         } else {
40381             this.stripEl.show();
40382         }
40383         
40384         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40385             return;
40386         }
40387         
40388         
40389         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40390         var availWidth = Math.floor(w / vcount);
40391         var b = this.stripBody;
40392         if(b.getWidth() > w){
40393             var tabs = this.items;
40394             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40395             if(availWidth < this.minTabWidth){
40396                 /*if(!this.sleft){    // incomplete scrolling code
40397                     this.createScrollButtons();
40398                 }
40399                 this.showScroll();
40400                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40401             }
40402         }else{
40403             if(this.currentTabWidth < this.preferredTabWidth){
40404                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40405             }
40406         }
40407     },
40408
40409     /**
40410      * Returns the number of tabs in this TabPanel.
40411      * @return {Number}
40412      */
40413      getCount : function(){
40414          return this.items.length;
40415      },
40416
40417     /**
40418      * Resizes all the tabs to the passed width
40419      * @param {Number} The new width
40420      */
40421     setTabWidth : function(width){
40422         this.currentTabWidth = width;
40423         for(var i = 0, len = this.items.length; i < len; i++) {
40424                 if(!this.items[i].isHidden()) {
40425                 this.items[i].setWidth(width);
40426             }
40427         }
40428     },
40429
40430     /**
40431      * Destroys this TabPanel
40432      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40433      */
40434     destroy : function(removeEl){
40435         Roo.EventManager.removeResizeListener(this.onResize, this);
40436         for(var i = 0, len = this.items.length; i < len; i++){
40437             this.items[i].purgeListeners();
40438         }
40439         if(removeEl === true){
40440             this.el.update("");
40441             this.el.remove();
40442         }
40443     },
40444     
40445     createStrip : function(container)
40446     {
40447         var strip = document.createElement("nav");
40448         strip.className = Roo.bootstrap.version == 4 ?
40449             "navbar-light bg-light" : 
40450             "navbar navbar-default"; //"x-tabs-wrap";
40451         container.appendChild(strip);
40452         return strip;
40453     },
40454     
40455     createStripList : function(strip)
40456     {
40457         // div wrapper for retard IE
40458         // returns the "tr" element.
40459         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40460         //'<div class="x-tabs-strip-wrap">'+
40461           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40462           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40463         return strip.firstChild; //.firstChild.firstChild.firstChild;
40464     },
40465     createBody : function(container)
40466     {
40467         var body = document.createElement("div");
40468         Roo.id(body, "tab-body");
40469         //Roo.fly(body).addClass("x-tabs-body");
40470         Roo.fly(body).addClass("tab-content");
40471         container.appendChild(body);
40472         return body;
40473     },
40474     createItemBody :function(bodyEl, id){
40475         var body = Roo.getDom(id);
40476         if(!body){
40477             body = document.createElement("div");
40478             body.id = id;
40479         }
40480         //Roo.fly(body).addClass("x-tabs-item-body");
40481         Roo.fly(body).addClass("tab-pane");
40482          bodyEl.insertBefore(body, bodyEl.firstChild);
40483         return body;
40484     },
40485     /** @private */
40486     createStripElements :  function(stripEl, text, closable, tpl)
40487     {
40488         var td = document.createElement("li"); // was td..
40489         td.className = 'nav-item';
40490         
40491         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40492         
40493         
40494         stripEl.appendChild(td);
40495         /*if(closable){
40496             td.className = "x-tabs-closable";
40497             if(!this.closeTpl){
40498                 this.closeTpl = new Roo.Template(
40499                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40500                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40501                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40502                 );
40503             }
40504             var el = this.closeTpl.overwrite(td, {"text": text});
40505             var close = el.getElementsByTagName("div")[0];
40506             var inner = el.getElementsByTagName("em")[0];
40507             return {"el": el, "close": close, "inner": inner};
40508         } else {
40509         */
40510         // not sure what this is..
40511 //            if(!this.tabTpl){
40512                 //this.tabTpl = new Roo.Template(
40513                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40514                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40515                 //);
40516 //                this.tabTpl = new Roo.Template(
40517 //                   '<a href="#">' +
40518 //                   '<span unselectable="on"' +
40519 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40520 //                            ' >{text}</span></a>'
40521 //                );
40522 //                
40523 //            }
40524
40525
40526             var template = tpl || this.tabTpl || false;
40527             
40528             if(!template){
40529                 template =  new Roo.Template(
40530                         Roo.bootstrap.version == 4 ? 
40531                             (
40532                                 '<a class="nav-link" href="#" unselectable="on"' +
40533                                      (this.disableTooltips ? '' : ' title="{text}"') +
40534                                      ' >{text}</a>'
40535                             ) : (
40536                                 '<a class="nav-link" href="#">' +
40537                                 '<span unselectable="on"' +
40538                                          (this.disableTooltips ? '' : ' title="{text}"') +
40539                                     ' >{text}</span></a>'
40540                             )
40541                 );
40542             }
40543             
40544             switch (typeof(template)) {
40545                 case 'object' :
40546                     break;
40547                 case 'string' :
40548                     template = new Roo.Template(template);
40549                     break;
40550                 default :
40551                     break;
40552             }
40553             
40554             var el = template.overwrite(td, {"text": text});
40555             
40556             var inner = el.getElementsByTagName("span")[0];
40557             
40558             return {"el": el, "inner": inner};
40559             
40560     }
40561         
40562     
40563 });
40564
40565 /**
40566  * @class Roo.TabPanelItem
40567  * @extends Roo.util.Observable
40568  * Represents an individual item (tab plus body) in a TabPanel.
40569  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40570  * @param {String} id The id of this TabPanelItem
40571  * @param {String} text The text for the tab of this TabPanelItem
40572  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40573  */
40574 Roo.bootstrap.panel.TabItem = function(config){
40575     /**
40576      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40577      * @type Roo.TabPanel
40578      */
40579     this.tabPanel = config.panel;
40580     /**
40581      * The id for this TabPanelItem
40582      * @type String
40583      */
40584     this.id = config.id;
40585     /** @private */
40586     this.disabled = false;
40587     /** @private */
40588     this.text = config.text;
40589     /** @private */
40590     this.loaded = false;
40591     this.closable = config.closable;
40592
40593     /**
40594      * The body element for this TabPanelItem.
40595      * @type Roo.Element
40596      */
40597     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40598     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40599     this.bodyEl.setStyle("display", "block");
40600     this.bodyEl.setStyle("zoom", "1");
40601     //this.hideAction();
40602
40603     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40604     /** @private */
40605     this.el = Roo.get(els.el);
40606     this.inner = Roo.get(els.inner, true);
40607      this.textEl = Roo.bootstrap.version == 4 ?
40608         this.el : Roo.get(this.el.dom.firstChild, true);
40609
40610     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40611     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40612
40613     
40614 //    this.el.on("mousedown", this.onTabMouseDown, this);
40615     this.el.on("click", this.onTabClick, this);
40616     /** @private */
40617     if(config.closable){
40618         var c = Roo.get(els.close, true);
40619         c.dom.title = this.closeText;
40620         c.addClassOnOver("close-over");
40621         c.on("click", this.closeClick, this);
40622      }
40623
40624     this.addEvents({
40625          /**
40626          * @event activate
40627          * Fires when this tab becomes the active tab.
40628          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40629          * @param {Roo.TabPanelItem} this
40630          */
40631         "activate": true,
40632         /**
40633          * @event beforeclose
40634          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40635          * @param {Roo.TabPanelItem} this
40636          * @param {Object} e Set cancel to true on this object to cancel the close.
40637          */
40638         "beforeclose": true,
40639         /**
40640          * @event close
40641          * Fires when this tab is closed.
40642          * @param {Roo.TabPanelItem} this
40643          */
40644          "close": true,
40645         /**
40646          * @event deactivate
40647          * Fires when this tab is no longer the active tab.
40648          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40649          * @param {Roo.TabPanelItem} this
40650          */
40651          "deactivate" : true
40652     });
40653     this.hidden = false;
40654
40655     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40656 };
40657
40658 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40659            {
40660     purgeListeners : function(){
40661        Roo.util.Observable.prototype.purgeListeners.call(this);
40662        this.el.removeAllListeners();
40663     },
40664     /**
40665      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40666      */
40667     show : function(){
40668         this.status_node.addClass("active");
40669         this.showAction();
40670         if(Roo.isOpera){
40671             this.tabPanel.stripWrap.repaint();
40672         }
40673         this.fireEvent("activate", this.tabPanel, this);
40674     },
40675
40676     /**
40677      * Returns true if this tab is the active tab.
40678      * @return {Boolean}
40679      */
40680     isActive : function(){
40681         return this.tabPanel.getActiveTab() == this;
40682     },
40683
40684     /**
40685      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40686      */
40687     hide : function(){
40688         this.status_node.removeClass("active");
40689         this.hideAction();
40690         this.fireEvent("deactivate", this.tabPanel, this);
40691     },
40692
40693     hideAction : function(){
40694         this.bodyEl.hide();
40695         this.bodyEl.setStyle("position", "absolute");
40696         this.bodyEl.setLeft("-20000px");
40697         this.bodyEl.setTop("-20000px");
40698     },
40699
40700     showAction : function(){
40701         this.bodyEl.setStyle("position", "relative");
40702         this.bodyEl.setTop("");
40703         this.bodyEl.setLeft("");
40704         this.bodyEl.show();
40705     },
40706
40707     /**
40708      * Set the tooltip for the tab.
40709      * @param {String} tooltip The tab's tooltip
40710      */
40711     setTooltip : function(text){
40712         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40713             this.textEl.dom.qtip = text;
40714             this.textEl.dom.removeAttribute('title');
40715         }else{
40716             this.textEl.dom.title = text;
40717         }
40718     },
40719
40720     onTabClick : function(e){
40721         e.preventDefault();
40722         this.tabPanel.activate(this.id);
40723     },
40724
40725     onTabMouseDown : function(e){
40726         e.preventDefault();
40727         this.tabPanel.activate(this.id);
40728     },
40729 /*
40730     getWidth : function(){
40731         return this.inner.getWidth();
40732     },
40733
40734     setWidth : function(width){
40735         var iwidth = width - this.linode.getPadding("lr");
40736         this.inner.setWidth(iwidth);
40737         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40738         this.linode.setWidth(width);
40739     },
40740 */
40741     /**
40742      * Show or hide the tab
40743      * @param {Boolean} hidden True to hide or false to show.
40744      */
40745     setHidden : function(hidden){
40746         this.hidden = hidden;
40747         this.linode.setStyle("display", hidden ? "none" : "");
40748     },
40749
40750     /**
40751      * Returns true if this tab is "hidden"
40752      * @return {Boolean}
40753      */
40754     isHidden : function(){
40755         return this.hidden;
40756     },
40757
40758     /**
40759      * Returns the text for this tab
40760      * @return {String}
40761      */
40762     getText : function(){
40763         return this.text;
40764     },
40765     /*
40766     autoSize : function(){
40767         //this.el.beginMeasure();
40768         this.textEl.setWidth(1);
40769         /*
40770          *  #2804 [new] Tabs in Roojs
40771          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40772          */
40773         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40774         //this.el.endMeasure();
40775     //},
40776
40777     /**
40778      * Sets the text for the tab (Note: this also sets the tooltip text)
40779      * @param {String} text The tab's text and tooltip
40780      */
40781     setText : function(text){
40782         this.text = text;
40783         this.textEl.update(text);
40784         this.setTooltip(text);
40785         //if(!this.tabPanel.resizeTabs){
40786         //    this.autoSize();
40787         //}
40788     },
40789     /**
40790      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40791      */
40792     activate : function(){
40793         this.tabPanel.activate(this.id);
40794     },
40795
40796     /**
40797      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40798      */
40799     disable : function(){
40800         if(this.tabPanel.active != this){
40801             this.disabled = true;
40802             this.status_node.addClass("disabled");
40803         }
40804     },
40805
40806     /**
40807      * Enables this TabPanelItem if it was previously disabled.
40808      */
40809     enable : function(){
40810         this.disabled = false;
40811         this.status_node.removeClass("disabled");
40812     },
40813
40814     /**
40815      * Sets the content for this TabPanelItem.
40816      * @param {String} content The content
40817      * @param {Boolean} loadScripts true to look for and load scripts
40818      */
40819     setContent : function(content, loadScripts){
40820         this.bodyEl.update(content, loadScripts);
40821     },
40822
40823     /**
40824      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40825      * @return {Roo.UpdateManager} The UpdateManager
40826      */
40827     getUpdateManager : function(){
40828         return this.bodyEl.getUpdateManager();
40829     },
40830
40831     /**
40832      * Set a URL to be used to load the content for this TabPanelItem.
40833      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40834      * @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)
40835      * @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)
40836      * @return {Roo.UpdateManager} The UpdateManager
40837      */
40838     setUrl : function(url, params, loadOnce){
40839         if(this.refreshDelegate){
40840             this.un('activate', this.refreshDelegate);
40841         }
40842         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40843         this.on("activate", this.refreshDelegate);
40844         return this.bodyEl.getUpdateManager();
40845     },
40846
40847     /** @private */
40848     _handleRefresh : function(url, params, loadOnce){
40849         if(!loadOnce || !this.loaded){
40850             var updater = this.bodyEl.getUpdateManager();
40851             updater.update(url, params, this._setLoaded.createDelegate(this));
40852         }
40853     },
40854
40855     /**
40856      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40857      *   Will fail silently if the setUrl method has not been called.
40858      *   This does not activate the panel, just updates its content.
40859      */
40860     refresh : function(){
40861         if(this.refreshDelegate){
40862            this.loaded = false;
40863            this.refreshDelegate();
40864         }
40865     },
40866
40867     /** @private */
40868     _setLoaded : function(){
40869         this.loaded = true;
40870     },
40871
40872     /** @private */
40873     closeClick : function(e){
40874         var o = {};
40875         e.stopEvent();
40876         this.fireEvent("beforeclose", this, o);
40877         if(o.cancel !== true){
40878             this.tabPanel.removeTab(this.id);
40879         }
40880     },
40881     /**
40882      * The text displayed in the tooltip for the close icon.
40883      * @type String
40884      */
40885     closeText : "Close this tab"
40886 });
40887 /**
40888 *    This script refer to:
40889 *    Title: International Telephone Input
40890 *    Author: Jack O'Connor
40891 *    Code version:  v12.1.12
40892 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40893 **/
40894
40895 Roo.bootstrap.PhoneInputData = function() {
40896     var d = [
40897       [
40898         "Afghanistan (‫افغانستان‬‎)",
40899         "af",
40900         "93"
40901       ],
40902       [
40903         "Albania (Shqipëri)",
40904         "al",
40905         "355"
40906       ],
40907       [
40908         "Algeria (‫الجزائر‬‎)",
40909         "dz",
40910         "213"
40911       ],
40912       [
40913         "American Samoa",
40914         "as",
40915         "1684"
40916       ],
40917       [
40918         "Andorra",
40919         "ad",
40920         "376"
40921       ],
40922       [
40923         "Angola",
40924         "ao",
40925         "244"
40926       ],
40927       [
40928         "Anguilla",
40929         "ai",
40930         "1264"
40931       ],
40932       [
40933         "Antigua and Barbuda",
40934         "ag",
40935         "1268"
40936       ],
40937       [
40938         "Argentina",
40939         "ar",
40940         "54"
40941       ],
40942       [
40943         "Armenia (Հայաստան)",
40944         "am",
40945         "374"
40946       ],
40947       [
40948         "Aruba",
40949         "aw",
40950         "297"
40951       ],
40952       [
40953         "Australia",
40954         "au",
40955         "61",
40956         0
40957       ],
40958       [
40959         "Austria (Österreich)",
40960         "at",
40961         "43"
40962       ],
40963       [
40964         "Azerbaijan (Azərbaycan)",
40965         "az",
40966         "994"
40967       ],
40968       [
40969         "Bahamas",
40970         "bs",
40971         "1242"
40972       ],
40973       [
40974         "Bahrain (‫البحرين‬‎)",
40975         "bh",
40976         "973"
40977       ],
40978       [
40979         "Bangladesh (বাংলাদেশ)",
40980         "bd",
40981         "880"
40982       ],
40983       [
40984         "Barbados",
40985         "bb",
40986         "1246"
40987       ],
40988       [
40989         "Belarus (Беларусь)",
40990         "by",
40991         "375"
40992       ],
40993       [
40994         "Belgium (België)",
40995         "be",
40996         "32"
40997       ],
40998       [
40999         "Belize",
41000         "bz",
41001         "501"
41002       ],
41003       [
41004         "Benin (Bénin)",
41005         "bj",
41006         "229"
41007       ],
41008       [
41009         "Bermuda",
41010         "bm",
41011         "1441"
41012       ],
41013       [
41014         "Bhutan (འབྲུག)",
41015         "bt",
41016         "975"
41017       ],
41018       [
41019         "Bolivia",
41020         "bo",
41021         "591"
41022       ],
41023       [
41024         "Bosnia and Herzegovina (Босна и Херцеговина)",
41025         "ba",
41026         "387"
41027       ],
41028       [
41029         "Botswana",
41030         "bw",
41031         "267"
41032       ],
41033       [
41034         "Brazil (Brasil)",
41035         "br",
41036         "55"
41037       ],
41038       [
41039         "British Indian Ocean Territory",
41040         "io",
41041         "246"
41042       ],
41043       [
41044         "British Virgin Islands",
41045         "vg",
41046         "1284"
41047       ],
41048       [
41049         "Brunei",
41050         "bn",
41051         "673"
41052       ],
41053       [
41054         "Bulgaria (България)",
41055         "bg",
41056         "359"
41057       ],
41058       [
41059         "Burkina Faso",
41060         "bf",
41061         "226"
41062       ],
41063       [
41064         "Burundi (Uburundi)",
41065         "bi",
41066         "257"
41067       ],
41068       [
41069         "Cambodia (កម្ពុជា)",
41070         "kh",
41071         "855"
41072       ],
41073       [
41074         "Cameroon (Cameroun)",
41075         "cm",
41076         "237"
41077       ],
41078       [
41079         "Canada",
41080         "ca",
41081         "1",
41082         1,
41083         ["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"]
41084       ],
41085       [
41086         "Cape Verde (Kabu Verdi)",
41087         "cv",
41088         "238"
41089       ],
41090       [
41091         "Caribbean Netherlands",
41092         "bq",
41093         "599",
41094         1
41095       ],
41096       [
41097         "Cayman Islands",
41098         "ky",
41099         "1345"
41100       ],
41101       [
41102         "Central African Republic (République centrafricaine)",
41103         "cf",
41104         "236"
41105       ],
41106       [
41107         "Chad (Tchad)",
41108         "td",
41109         "235"
41110       ],
41111       [
41112         "Chile",
41113         "cl",
41114         "56"
41115       ],
41116       [
41117         "China (中国)",
41118         "cn",
41119         "86"
41120       ],
41121       [
41122         "Christmas Island",
41123         "cx",
41124         "61",
41125         2
41126       ],
41127       [
41128         "Cocos (Keeling) Islands",
41129         "cc",
41130         "61",
41131         1
41132       ],
41133       [
41134         "Colombia",
41135         "co",
41136         "57"
41137       ],
41138       [
41139         "Comoros (‫جزر القمر‬‎)",
41140         "km",
41141         "269"
41142       ],
41143       [
41144         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41145         "cd",
41146         "243"
41147       ],
41148       [
41149         "Congo (Republic) (Congo-Brazzaville)",
41150         "cg",
41151         "242"
41152       ],
41153       [
41154         "Cook Islands",
41155         "ck",
41156         "682"
41157       ],
41158       [
41159         "Costa Rica",
41160         "cr",
41161         "506"
41162       ],
41163       [
41164         "Côte d’Ivoire",
41165         "ci",
41166         "225"
41167       ],
41168       [
41169         "Croatia (Hrvatska)",
41170         "hr",
41171         "385"
41172       ],
41173       [
41174         "Cuba",
41175         "cu",
41176         "53"
41177       ],
41178       [
41179         "Curaçao",
41180         "cw",
41181         "599",
41182         0
41183       ],
41184       [
41185         "Cyprus (Κύπρος)",
41186         "cy",
41187         "357"
41188       ],
41189       [
41190         "Czech Republic (Česká republika)",
41191         "cz",
41192         "420"
41193       ],
41194       [
41195         "Denmark (Danmark)",
41196         "dk",
41197         "45"
41198       ],
41199       [
41200         "Djibouti",
41201         "dj",
41202         "253"
41203       ],
41204       [
41205         "Dominica",
41206         "dm",
41207         "1767"
41208       ],
41209       [
41210         "Dominican Republic (República Dominicana)",
41211         "do",
41212         "1",
41213         2,
41214         ["809", "829", "849"]
41215       ],
41216       [
41217         "Ecuador",
41218         "ec",
41219         "593"
41220       ],
41221       [
41222         "Egypt (‫مصر‬‎)",
41223         "eg",
41224         "20"
41225       ],
41226       [
41227         "El Salvador",
41228         "sv",
41229         "503"
41230       ],
41231       [
41232         "Equatorial Guinea (Guinea Ecuatorial)",
41233         "gq",
41234         "240"
41235       ],
41236       [
41237         "Eritrea",
41238         "er",
41239         "291"
41240       ],
41241       [
41242         "Estonia (Eesti)",
41243         "ee",
41244         "372"
41245       ],
41246       [
41247         "Ethiopia",
41248         "et",
41249         "251"
41250       ],
41251       [
41252         "Falkland Islands (Islas Malvinas)",
41253         "fk",
41254         "500"
41255       ],
41256       [
41257         "Faroe Islands (Føroyar)",
41258         "fo",
41259         "298"
41260       ],
41261       [
41262         "Fiji",
41263         "fj",
41264         "679"
41265       ],
41266       [
41267         "Finland (Suomi)",
41268         "fi",
41269         "358",
41270         0
41271       ],
41272       [
41273         "France",
41274         "fr",
41275         "33"
41276       ],
41277       [
41278         "French Guiana (Guyane française)",
41279         "gf",
41280         "594"
41281       ],
41282       [
41283         "French Polynesia (Polynésie française)",
41284         "pf",
41285         "689"
41286       ],
41287       [
41288         "Gabon",
41289         "ga",
41290         "241"
41291       ],
41292       [
41293         "Gambia",
41294         "gm",
41295         "220"
41296       ],
41297       [
41298         "Georgia (საქართველო)",
41299         "ge",
41300         "995"
41301       ],
41302       [
41303         "Germany (Deutschland)",
41304         "de",
41305         "49"
41306       ],
41307       [
41308         "Ghana (Gaana)",
41309         "gh",
41310         "233"
41311       ],
41312       [
41313         "Gibraltar",
41314         "gi",
41315         "350"
41316       ],
41317       [
41318         "Greece (Ελλάδα)",
41319         "gr",
41320         "30"
41321       ],
41322       [
41323         "Greenland (Kalaallit Nunaat)",
41324         "gl",
41325         "299"
41326       ],
41327       [
41328         "Grenada",
41329         "gd",
41330         "1473"
41331       ],
41332       [
41333         "Guadeloupe",
41334         "gp",
41335         "590",
41336         0
41337       ],
41338       [
41339         "Guam",
41340         "gu",
41341         "1671"
41342       ],
41343       [
41344         "Guatemala",
41345         "gt",
41346         "502"
41347       ],
41348       [
41349         "Guernsey",
41350         "gg",
41351         "44",
41352         1
41353       ],
41354       [
41355         "Guinea (Guinée)",
41356         "gn",
41357         "224"
41358       ],
41359       [
41360         "Guinea-Bissau (Guiné Bissau)",
41361         "gw",
41362         "245"
41363       ],
41364       [
41365         "Guyana",
41366         "gy",
41367         "592"
41368       ],
41369       [
41370         "Haiti",
41371         "ht",
41372         "509"
41373       ],
41374       [
41375         "Honduras",
41376         "hn",
41377         "504"
41378       ],
41379       [
41380         "Hong Kong (香港)",
41381         "hk",
41382         "852"
41383       ],
41384       [
41385         "Hungary (Magyarország)",
41386         "hu",
41387         "36"
41388       ],
41389       [
41390         "Iceland (Ísland)",
41391         "is",
41392         "354"
41393       ],
41394       [
41395         "India (भारत)",
41396         "in",
41397         "91"
41398       ],
41399       [
41400         "Indonesia",
41401         "id",
41402         "62"
41403       ],
41404       [
41405         "Iran (‫ایران‬‎)",
41406         "ir",
41407         "98"
41408       ],
41409       [
41410         "Iraq (‫العراق‬‎)",
41411         "iq",
41412         "964"
41413       ],
41414       [
41415         "Ireland",
41416         "ie",
41417         "353"
41418       ],
41419       [
41420         "Isle of Man",
41421         "im",
41422         "44",
41423         2
41424       ],
41425       [
41426         "Israel (‫ישראל‬‎)",
41427         "il",
41428         "972"
41429       ],
41430       [
41431         "Italy (Italia)",
41432         "it",
41433         "39",
41434         0
41435       ],
41436       [
41437         "Jamaica",
41438         "jm",
41439         "1876"
41440       ],
41441       [
41442         "Japan (日本)",
41443         "jp",
41444         "81"
41445       ],
41446       [
41447         "Jersey",
41448         "je",
41449         "44",
41450         3
41451       ],
41452       [
41453         "Jordan (‫الأردن‬‎)",
41454         "jo",
41455         "962"
41456       ],
41457       [
41458         "Kazakhstan (Казахстан)",
41459         "kz",
41460         "7",
41461         1
41462       ],
41463       [
41464         "Kenya",
41465         "ke",
41466         "254"
41467       ],
41468       [
41469         "Kiribati",
41470         "ki",
41471         "686"
41472       ],
41473       [
41474         "Kosovo",
41475         "xk",
41476         "383"
41477       ],
41478       [
41479         "Kuwait (‫الكويت‬‎)",
41480         "kw",
41481         "965"
41482       ],
41483       [
41484         "Kyrgyzstan (Кыргызстан)",
41485         "kg",
41486         "996"
41487       ],
41488       [
41489         "Laos (ລາວ)",
41490         "la",
41491         "856"
41492       ],
41493       [
41494         "Latvia (Latvija)",
41495         "lv",
41496         "371"
41497       ],
41498       [
41499         "Lebanon (‫لبنان‬‎)",
41500         "lb",
41501         "961"
41502       ],
41503       [
41504         "Lesotho",
41505         "ls",
41506         "266"
41507       ],
41508       [
41509         "Liberia",
41510         "lr",
41511         "231"
41512       ],
41513       [
41514         "Libya (‫ليبيا‬‎)",
41515         "ly",
41516         "218"
41517       ],
41518       [
41519         "Liechtenstein",
41520         "li",
41521         "423"
41522       ],
41523       [
41524         "Lithuania (Lietuva)",
41525         "lt",
41526         "370"
41527       ],
41528       [
41529         "Luxembourg",
41530         "lu",
41531         "352"
41532       ],
41533       [
41534         "Macau (澳門)",
41535         "mo",
41536         "853"
41537       ],
41538       [
41539         "Macedonia (FYROM) (Македонија)",
41540         "mk",
41541         "389"
41542       ],
41543       [
41544         "Madagascar (Madagasikara)",
41545         "mg",
41546         "261"
41547       ],
41548       [
41549         "Malawi",
41550         "mw",
41551         "265"
41552       ],
41553       [
41554         "Malaysia",
41555         "my",
41556         "60"
41557       ],
41558       [
41559         "Maldives",
41560         "mv",
41561         "960"
41562       ],
41563       [
41564         "Mali",
41565         "ml",
41566         "223"
41567       ],
41568       [
41569         "Malta",
41570         "mt",
41571         "356"
41572       ],
41573       [
41574         "Marshall Islands",
41575         "mh",
41576         "692"
41577       ],
41578       [
41579         "Martinique",
41580         "mq",
41581         "596"
41582       ],
41583       [
41584         "Mauritania (‫موريتانيا‬‎)",
41585         "mr",
41586         "222"
41587       ],
41588       [
41589         "Mauritius (Moris)",
41590         "mu",
41591         "230"
41592       ],
41593       [
41594         "Mayotte",
41595         "yt",
41596         "262",
41597         1
41598       ],
41599       [
41600         "Mexico (México)",
41601         "mx",
41602         "52"
41603       ],
41604       [
41605         "Micronesia",
41606         "fm",
41607         "691"
41608       ],
41609       [
41610         "Moldova (Republica Moldova)",
41611         "md",
41612         "373"
41613       ],
41614       [
41615         "Monaco",
41616         "mc",
41617         "377"
41618       ],
41619       [
41620         "Mongolia (Монгол)",
41621         "mn",
41622         "976"
41623       ],
41624       [
41625         "Montenegro (Crna Gora)",
41626         "me",
41627         "382"
41628       ],
41629       [
41630         "Montserrat",
41631         "ms",
41632         "1664"
41633       ],
41634       [
41635         "Morocco (‫المغرب‬‎)",
41636         "ma",
41637         "212",
41638         0
41639       ],
41640       [
41641         "Mozambique (Moçambique)",
41642         "mz",
41643         "258"
41644       ],
41645       [
41646         "Myanmar (Burma) (မြန်မာ)",
41647         "mm",
41648         "95"
41649       ],
41650       [
41651         "Namibia (Namibië)",
41652         "na",
41653         "264"
41654       ],
41655       [
41656         "Nauru",
41657         "nr",
41658         "674"
41659       ],
41660       [
41661         "Nepal (नेपाल)",
41662         "np",
41663         "977"
41664       ],
41665       [
41666         "Netherlands (Nederland)",
41667         "nl",
41668         "31"
41669       ],
41670       [
41671         "New Caledonia (Nouvelle-Calédonie)",
41672         "nc",
41673         "687"
41674       ],
41675       [
41676         "New Zealand",
41677         "nz",
41678         "64"
41679       ],
41680       [
41681         "Nicaragua",
41682         "ni",
41683         "505"
41684       ],
41685       [
41686         "Niger (Nijar)",
41687         "ne",
41688         "227"
41689       ],
41690       [
41691         "Nigeria",
41692         "ng",
41693         "234"
41694       ],
41695       [
41696         "Niue",
41697         "nu",
41698         "683"
41699       ],
41700       [
41701         "Norfolk Island",
41702         "nf",
41703         "672"
41704       ],
41705       [
41706         "North Korea (조선 민주주의 인민 공화국)",
41707         "kp",
41708         "850"
41709       ],
41710       [
41711         "Northern Mariana Islands",
41712         "mp",
41713         "1670"
41714       ],
41715       [
41716         "Norway (Norge)",
41717         "no",
41718         "47",
41719         0
41720       ],
41721       [
41722         "Oman (‫عُمان‬‎)",
41723         "om",
41724         "968"
41725       ],
41726       [
41727         "Pakistan (‫پاکستان‬‎)",
41728         "pk",
41729         "92"
41730       ],
41731       [
41732         "Palau",
41733         "pw",
41734         "680"
41735       ],
41736       [
41737         "Palestine (‫فلسطين‬‎)",
41738         "ps",
41739         "970"
41740       ],
41741       [
41742         "Panama (Panamá)",
41743         "pa",
41744         "507"
41745       ],
41746       [
41747         "Papua New Guinea",
41748         "pg",
41749         "675"
41750       ],
41751       [
41752         "Paraguay",
41753         "py",
41754         "595"
41755       ],
41756       [
41757         "Peru (Perú)",
41758         "pe",
41759         "51"
41760       ],
41761       [
41762         "Philippines",
41763         "ph",
41764         "63"
41765       ],
41766       [
41767         "Poland (Polska)",
41768         "pl",
41769         "48"
41770       ],
41771       [
41772         "Portugal",
41773         "pt",
41774         "351"
41775       ],
41776       [
41777         "Puerto Rico",
41778         "pr",
41779         "1",
41780         3,
41781         ["787", "939"]
41782       ],
41783       [
41784         "Qatar (‫قطر‬‎)",
41785         "qa",
41786         "974"
41787       ],
41788       [
41789         "Réunion (La Réunion)",
41790         "re",
41791         "262",
41792         0
41793       ],
41794       [
41795         "Romania (România)",
41796         "ro",
41797         "40"
41798       ],
41799       [
41800         "Russia (Россия)",
41801         "ru",
41802         "7",
41803         0
41804       ],
41805       [
41806         "Rwanda",
41807         "rw",
41808         "250"
41809       ],
41810       [
41811         "Saint Barthélemy",
41812         "bl",
41813         "590",
41814         1
41815       ],
41816       [
41817         "Saint Helena",
41818         "sh",
41819         "290"
41820       ],
41821       [
41822         "Saint Kitts and Nevis",
41823         "kn",
41824         "1869"
41825       ],
41826       [
41827         "Saint Lucia",
41828         "lc",
41829         "1758"
41830       ],
41831       [
41832         "Saint Martin (Saint-Martin (partie française))",
41833         "mf",
41834         "590",
41835         2
41836       ],
41837       [
41838         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41839         "pm",
41840         "508"
41841       ],
41842       [
41843         "Saint Vincent and the Grenadines",
41844         "vc",
41845         "1784"
41846       ],
41847       [
41848         "Samoa",
41849         "ws",
41850         "685"
41851       ],
41852       [
41853         "San Marino",
41854         "sm",
41855         "378"
41856       ],
41857       [
41858         "São Tomé and Príncipe (São Tomé e Príncipe)",
41859         "st",
41860         "239"
41861       ],
41862       [
41863         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41864         "sa",
41865         "966"
41866       ],
41867       [
41868         "Senegal (Sénégal)",
41869         "sn",
41870         "221"
41871       ],
41872       [
41873         "Serbia (Србија)",
41874         "rs",
41875         "381"
41876       ],
41877       [
41878         "Seychelles",
41879         "sc",
41880         "248"
41881       ],
41882       [
41883         "Sierra Leone",
41884         "sl",
41885         "232"
41886       ],
41887       [
41888         "Singapore",
41889         "sg",
41890         "65"
41891       ],
41892       [
41893         "Sint Maarten",
41894         "sx",
41895         "1721"
41896       ],
41897       [
41898         "Slovakia (Slovensko)",
41899         "sk",
41900         "421"
41901       ],
41902       [
41903         "Slovenia (Slovenija)",
41904         "si",
41905         "386"
41906       ],
41907       [
41908         "Solomon Islands",
41909         "sb",
41910         "677"
41911       ],
41912       [
41913         "Somalia (Soomaaliya)",
41914         "so",
41915         "252"
41916       ],
41917       [
41918         "South Africa",
41919         "za",
41920         "27"
41921       ],
41922       [
41923         "South Korea (대한민국)",
41924         "kr",
41925         "82"
41926       ],
41927       [
41928         "South Sudan (‫جنوب السودان‬‎)",
41929         "ss",
41930         "211"
41931       ],
41932       [
41933         "Spain (España)",
41934         "es",
41935         "34"
41936       ],
41937       [
41938         "Sri Lanka (ශ්‍රී ලංකාව)",
41939         "lk",
41940         "94"
41941       ],
41942       [
41943         "Sudan (‫السودان‬‎)",
41944         "sd",
41945         "249"
41946       ],
41947       [
41948         "Suriname",
41949         "sr",
41950         "597"
41951       ],
41952       [
41953         "Svalbard and Jan Mayen",
41954         "sj",
41955         "47",
41956         1
41957       ],
41958       [
41959         "Swaziland",
41960         "sz",
41961         "268"
41962       ],
41963       [
41964         "Sweden (Sverige)",
41965         "se",
41966         "46"
41967       ],
41968       [
41969         "Switzerland (Schweiz)",
41970         "ch",
41971         "41"
41972       ],
41973       [
41974         "Syria (‫سوريا‬‎)",
41975         "sy",
41976         "963"
41977       ],
41978       [
41979         "Taiwan (台灣)",
41980         "tw",
41981         "886"
41982       ],
41983       [
41984         "Tajikistan",
41985         "tj",
41986         "992"
41987       ],
41988       [
41989         "Tanzania",
41990         "tz",
41991         "255"
41992       ],
41993       [
41994         "Thailand (ไทย)",
41995         "th",
41996         "66"
41997       ],
41998       [
41999         "Timor-Leste",
42000         "tl",
42001         "670"
42002       ],
42003       [
42004         "Togo",
42005         "tg",
42006         "228"
42007       ],
42008       [
42009         "Tokelau",
42010         "tk",
42011         "690"
42012       ],
42013       [
42014         "Tonga",
42015         "to",
42016         "676"
42017       ],
42018       [
42019         "Trinidad and Tobago",
42020         "tt",
42021         "1868"
42022       ],
42023       [
42024         "Tunisia (‫تونس‬‎)",
42025         "tn",
42026         "216"
42027       ],
42028       [
42029         "Turkey (Türkiye)",
42030         "tr",
42031         "90"
42032       ],
42033       [
42034         "Turkmenistan",
42035         "tm",
42036         "993"
42037       ],
42038       [
42039         "Turks and Caicos Islands",
42040         "tc",
42041         "1649"
42042       ],
42043       [
42044         "Tuvalu",
42045         "tv",
42046         "688"
42047       ],
42048       [
42049         "U.S. Virgin Islands",
42050         "vi",
42051         "1340"
42052       ],
42053       [
42054         "Uganda",
42055         "ug",
42056         "256"
42057       ],
42058       [
42059         "Ukraine (Україна)",
42060         "ua",
42061         "380"
42062       ],
42063       [
42064         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42065         "ae",
42066         "971"
42067       ],
42068       [
42069         "United Kingdom",
42070         "gb",
42071         "44",
42072         0
42073       ],
42074       [
42075         "United States",
42076         "us",
42077         "1",
42078         0
42079       ],
42080       [
42081         "Uruguay",
42082         "uy",
42083         "598"
42084       ],
42085       [
42086         "Uzbekistan (Oʻzbekiston)",
42087         "uz",
42088         "998"
42089       ],
42090       [
42091         "Vanuatu",
42092         "vu",
42093         "678"
42094       ],
42095       [
42096         "Vatican City (Città del Vaticano)",
42097         "va",
42098         "39",
42099         1
42100       ],
42101       [
42102         "Venezuela",
42103         "ve",
42104         "58"
42105       ],
42106       [
42107         "Vietnam (Việt Nam)",
42108         "vn",
42109         "84"
42110       ],
42111       [
42112         "Wallis and Futuna (Wallis-et-Futuna)",
42113         "wf",
42114         "681"
42115       ],
42116       [
42117         "Western Sahara (‫الصحراء الغربية‬‎)",
42118         "eh",
42119         "212",
42120         1
42121       ],
42122       [
42123         "Yemen (‫اليمن‬‎)",
42124         "ye",
42125         "967"
42126       ],
42127       [
42128         "Zambia",
42129         "zm",
42130         "260"
42131       ],
42132       [
42133         "Zimbabwe",
42134         "zw",
42135         "263"
42136       ],
42137       [
42138         "Åland Islands",
42139         "ax",
42140         "358",
42141         1
42142       ]
42143   ];
42144   
42145   return d;
42146 }/**
42147 *    This script refer to:
42148 *    Title: International Telephone Input
42149 *    Author: Jack O'Connor
42150 *    Code version:  v12.1.12
42151 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42152 **/
42153
42154 /**
42155  * @class Roo.bootstrap.PhoneInput
42156  * @extends Roo.bootstrap.TriggerField
42157  * An input with International dial-code selection
42158  
42159  * @cfg {String} defaultDialCode default '+852'
42160  * @cfg {Array} preferedCountries default []
42161   
42162  * @constructor
42163  * Create a new PhoneInput.
42164  * @param {Object} config Configuration options
42165  */
42166
42167 Roo.bootstrap.PhoneInput = function(config) {
42168     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42169 };
42170
42171 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42172         
42173         listWidth: undefined,
42174         
42175         selectedClass: 'active',
42176         
42177         invalidClass : "has-warning",
42178         
42179         validClass: 'has-success',
42180         
42181         allowed: '0123456789',
42182         
42183         max_length: 15,
42184         
42185         /**
42186          * @cfg {String} defaultDialCode The default dial code when initializing the input
42187          */
42188         defaultDialCode: '+852',
42189         
42190         /**
42191          * @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
42192          */
42193         preferedCountries: false,
42194         
42195         getAutoCreate : function()
42196         {
42197             var data = Roo.bootstrap.PhoneInputData();
42198             var align = this.labelAlign || this.parentLabelAlign();
42199             var id = Roo.id();
42200             
42201             this.allCountries = [];
42202             this.dialCodeMapping = [];
42203             
42204             for (var i = 0; i < data.length; i++) {
42205               var c = data[i];
42206               this.allCountries[i] = {
42207                 name: c[0],
42208                 iso2: c[1],
42209                 dialCode: c[2],
42210                 priority: c[3] || 0,
42211                 areaCodes: c[4] || null
42212               };
42213               this.dialCodeMapping[c[2]] = {
42214                   name: c[0],
42215                   iso2: c[1],
42216                   priority: c[3] || 0,
42217                   areaCodes: c[4] || null
42218               };
42219             }
42220             
42221             var cfg = {
42222                 cls: 'form-group',
42223                 cn: []
42224             };
42225             
42226             var input =  {
42227                 tag: 'input',
42228                 id : id,
42229                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42230                 maxlength: this.max_length,
42231                 cls : 'form-control tel-input',
42232                 autocomplete: 'new-password'
42233             };
42234             
42235             var hiddenInput = {
42236                 tag: 'input',
42237                 type: 'hidden',
42238                 cls: 'hidden-tel-input'
42239             };
42240             
42241             if (this.name) {
42242                 hiddenInput.name = this.name;
42243             }
42244             
42245             if (this.disabled) {
42246                 input.disabled = true;
42247             }
42248             
42249             var flag_container = {
42250                 tag: 'div',
42251                 cls: 'flag-box',
42252                 cn: [
42253                     {
42254                         tag: 'div',
42255                         cls: 'flag'
42256                     },
42257                     {
42258                         tag: 'div',
42259                         cls: 'caret'
42260                     }
42261                 ]
42262             };
42263             
42264             var box = {
42265                 tag: 'div',
42266                 cls: this.hasFeedback ? 'has-feedback' : '',
42267                 cn: [
42268                     hiddenInput,
42269                     input,
42270                     {
42271                         tag: 'input',
42272                         cls: 'dial-code-holder',
42273                         disabled: true
42274                     }
42275                 ]
42276             };
42277             
42278             var container = {
42279                 cls: 'roo-select2-container input-group',
42280                 cn: [
42281                     flag_container,
42282                     box
42283                 ]
42284             };
42285             
42286             if (this.fieldLabel.length) {
42287                 var indicator = {
42288                     tag: 'i',
42289                     tooltip: 'This field is required'
42290                 };
42291                 
42292                 var label = {
42293                     tag: 'label',
42294                     'for':  id,
42295                     cls: 'control-label',
42296                     cn: []
42297                 };
42298                 
42299                 var label_text = {
42300                     tag: 'span',
42301                     html: this.fieldLabel
42302                 };
42303                 
42304                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42305                 label.cn = [
42306                     indicator,
42307                     label_text
42308                 ];
42309                 
42310                 if(this.indicatorpos == 'right') {
42311                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42312                     label.cn = [
42313                         label_text,
42314                         indicator
42315                     ];
42316                 }
42317                 
42318                 if(align == 'left') {
42319                     container = {
42320                         tag: 'div',
42321                         cn: [
42322                             container
42323                         ]
42324                     };
42325                     
42326                     if(this.labelWidth > 12){
42327                         label.style = "width: " + this.labelWidth + 'px';
42328                     }
42329                     if(this.labelWidth < 13 && this.labelmd == 0){
42330                         this.labelmd = this.labelWidth;
42331                     }
42332                     if(this.labellg > 0){
42333                         label.cls += ' col-lg-' + this.labellg;
42334                         input.cls += ' col-lg-' + (12 - this.labellg);
42335                     }
42336                     if(this.labelmd > 0){
42337                         label.cls += ' col-md-' + this.labelmd;
42338                         container.cls += ' col-md-' + (12 - this.labelmd);
42339                     }
42340                     if(this.labelsm > 0){
42341                         label.cls += ' col-sm-' + this.labelsm;
42342                         container.cls += ' col-sm-' + (12 - this.labelsm);
42343                     }
42344                     if(this.labelxs > 0){
42345                         label.cls += ' col-xs-' + this.labelxs;
42346                         container.cls += ' col-xs-' + (12 - this.labelxs);
42347                     }
42348                 }
42349             }
42350             
42351             cfg.cn = [
42352                 label,
42353                 container
42354             ];
42355             
42356             var settings = this;
42357             
42358             ['xs','sm','md','lg'].map(function(size){
42359                 if (settings[size]) {
42360                     cfg.cls += ' col-' + size + '-' + settings[size];
42361                 }
42362             });
42363             
42364             this.store = new Roo.data.Store({
42365                 proxy : new Roo.data.MemoryProxy({}),
42366                 reader : new Roo.data.JsonReader({
42367                     fields : [
42368                         {
42369                             'name' : 'name',
42370                             'type' : 'string'
42371                         },
42372                         {
42373                             'name' : 'iso2',
42374                             'type' : 'string'
42375                         },
42376                         {
42377                             'name' : 'dialCode',
42378                             'type' : 'string'
42379                         },
42380                         {
42381                             'name' : 'priority',
42382                             'type' : 'string'
42383                         },
42384                         {
42385                             'name' : 'areaCodes',
42386                             'type' : 'string'
42387                         }
42388                     ]
42389                 })
42390             });
42391             
42392             if(!this.preferedCountries) {
42393                 this.preferedCountries = [
42394                     'hk',
42395                     'gb',
42396                     'us'
42397                 ];
42398             }
42399             
42400             var p = this.preferedCountries.reverse();
42401             
42402             if(p) {
42403                 for (var i = 0; i < p.length; i++) {
42404                     for (var j = 0; j < this.allCountries.length; j++) {
42405                         if(this.allCountries[j].iso2 == p[i]) {
42406                             var t = this.allCountries[j];
42407                             this.allCountries.splice(j,1);
42408                             this.allCountries.unshift(t);
42409                         }
42410                     } 
42411                 }
42412             }
42413             
42414             this.store.proxy.data = {
42415                 success: true,
42416                 data: this.allCountries
42417             };
42418             
42419             return cfg;
42420         },
42421         
42422         initEvents : function()
42423         {
42424             this.createList();
42425             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42426             
42427             this.indicator = this.indicatorEl();
42428             this.flag = this.flagEl();
42429             this.dialCodeHolder = this.dialCodeHolderEl();
42430             
42431             this.trigger = this.el.select('div.flag-box',true).first();
42432             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42433             
42434             var _this = this;
42435             
42436             (function(){
42437                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42438                 _this.list.setWidth(lw);
42439             }).defer(100);
42440             
42441             this.list.on('mouseover', this.onViewOver, this);
42442             this.list.on('mousemove', this.onViewMove, this);
42443             this.inputEl().on("keyup", this.onKeyUp, this);
42444             this.inputEl().on("keypress", this.onKeyPress, this);
42445             
42446             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42447
42448             this.view = new Roo.View(this.list, this.tpl, {
42449                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42450             });
42451             
42452             this.view.on('click', this.onViewClick, this);
42453             this.setValue(this.defaultDialCode);
42454         },
42455         
42456         onTriggerClick : function(e)
42457         {
42458             Roo.log('trigger click');
42459             if(this.disabled){
42460                 return;
42461             }
42462             
42463             if(this.isExpanded()){
42464                 this.collapse();
42465                 this.hasFocus = false;
42466             }else {
42467                 this.store.load({});
42468                 this.hasFocus = true;
42469                 this.expand();
42470             }
42471         },
42472         
42473         isExpanded : function()
42474         {
42475             return this.list.isVisible();
42476         },
42477         
42478         collapse : function()
42479         {
42480             if(!this.isExpanded()){
42481                 return;
42482             }
42483             this.list.hide();
42484             Roo.get(document).un('mousedown', this.collapseIf, this);
42485             Roo.get(document).un('mousewheel', this.collapseIf, this);
42486             this.fireEvent('collapse', this);
42487             this.validate();
42488         },
42489         
42490         expand : function()
42491         {
42492             Roo.log('expand');
42493
42494             if(this.isExpanded() || !this.hasFocus){
42495                 return;
42496             }
42497             
42498             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42499             this.list.setWidth(lw);
42500             
42501             this.list.show();
42502             this.restrictHeight();
42503             
42504             Roo.get(document).on('mousedown', this.collapseIf, this);
42505             Roo.get(document).on('mousewheel', this.collapseIf, this);
42506             
42507             this.fireEvent('expand', this);
42508         },
42509         
42510         restrictHeight : function()
42511         {
42512             this.list.alignTo(this.inputEl(), this.listAlign);
42513             this.list.alignTo(this.inputEl(), this.listAlign);
42514         },
42515         
42516         onViewOver : function(e, t)
42517         {
42518             if(this.inKeyMode){
42519                 return;
42520             }
42521             var item = this.view.findItemFromChild(t);
42522             
42523             if(item){
42524                 var index = this.view.indexOf(item);
42525                 this.select(index, false);
42526             }
42527         },
42528
42529         // private
42530         onViewClick : function(view, doFocus, el, e)
42531         {
42532             var index = this.view.getSelectedIndexes()[0];
42533             
42534             var r = this.store.getAt(index);
42535             
42536             if(r){
42537                 this.onSelect(r, index);
42538             }
42539             if(doFocus !== false && !this.blockFocus){
42540                 this.inputEl().focus();
42541             }
42542         },
42543         
42544         onViewMove : function(e, t)
42545         {
42546             this.inKeyMode = false;
42547         },
42548         
42549         select : function(index, scrollIntoView)
42550         {
42551             this.selectedIndex = index;
42552             this.view.select(index);
42553             if(scrollIntoView !== false){
42554                 var el = this.view.getNode(index);
42555                 if(el){
42556                     this.list.scrollChildIntoView(el, false);
42557                 }
42558             }
42559         },
42560         
42561         createList : function()
42562         {
42563             this.list = Roo.get(document.body).createChild({
42564                 tag: 'ul',
42565                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42566                 style: 'display:none'
42567             });
42568             
42569             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42570         },
42571         
42572         collapseIf : function(e)
42573         {
42574             var in_combo  = e.within(this.el);
42575             var in_list =  e.within(this.list);
42576             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42577             
42578             if (in_combo || in_list || is_list) {
42579                 return;
42580             }
42581             this.collapse();
42582         },
42583         
42584         onSelect : function(record, index)
42585         {
42586             if(this.fireEvent('beforeselect', this, record, index) !== false){
42587                 
42588                 this.setFlagClass(record.data.iso2);
42589                 this.setDialCode(record.data.dialCode);
42590                 this.hasFocus = false;
42591                 this.collapse();
42592                 this.fireEvent('select', this, record, index);
42593             }
42594         },
42595         
42596         flagEl : function()
42597         {
42598             var flag = this.el.select('div.flag',true).first();
42599             if(!flag){
42600                 return false;
42601             }
42602             return flag;
42603         },
42604         
42605         dialCodeHolderEl : function()
42606         {
42607             var d = this.el.select('input.dial-code-holder',true).first();
42608             if(!d){
42609                 return false;
42610             }
42611             return d;
42612         },
42613         
42614         setDialCode : function(v)
42615         {
42616             this.dialCodeHolder.dom.value = '+'+v;
42617         },
42618         
42619         setFlagClass : function(n)
42620         {
42621             this.flag.dom.className = 'flag '+n;
42622         },
42623         
42624         getValue : function()
42625         {
42626             var v = this.inputEl().getValue();
42627             if(this.dialCodeHolder) {
42628                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42629             }
42630             return v;
42631         },
42632         
42633         setValue : function(v)
42634         {
42635             var d = this.getDialCode(v);
42636             
42637             //invalid dial code
42638             if(v.length == 0 || !d || d.length == 0) {
42639                 if(this.rendered){
42640                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42641                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42642                 }
42643                 return;
42644             }
42645             
42646             //valid dial code
42647             this.setFlagClass(this.dialCodeMapping[d].iso2);
42648             this.setDialCode(d);
42649             this.inputEl().dom.value = v.replace('+'+d,'');
42650             this.hiddenEl().dom.value = this.getValue();
42651             
42652             this.validate();
42653         },
42654         
42655         getDialCode : function(v)
42656         {
42657             v = v ||  '';
42658             
42659             if (v.length == 0) {
42660                 return this.dialCodeHolder.dom.value;
42661             }
42662             
42663             var dialCode = "";
42664             if (v.charAt(0) != "+") {
42665                 return false;
42666             }
42667             var numericChars = "";
42668             for (var i = 1; i < v.length; i++) {
42669               var c = v.charAt(i);
42670               if (!isNaN(c)) {
42671                 numericChars += c;
42672                 if (this.dialCodeMapping[numericChars]) {
42673                   dialCode = v.substr(1, i);
42674                 }
42675                 if (numericChars.length == 4) {
42676                   break;
42677                 }
42678               }
42679             }
42680             return dialCode;
42681         },
42682         
42683         reset : function()
42684         {
42685             this.setValue(this.defaultDialCode);
42686             this.validate();
42687         },
42688         
42689         hiddenEl : function()
42690         {
42691             return this.el.select('input.hidden-tel-input',true).first();
42692         },
42693         
42694         // after setting val
42695         onKeyUp : function(e){
42696             this.setValue(this.getValue());
42697         },
42698         
42699         onKeyPress : function(e){
42700             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42701                 e.stopEvent();
42702             }
42703         }
42704         
42705 });
42706 /**
42707  * @class Roo.bootstrap.MoneyField
42708  * @extends Roo.bootstrap.ComboBox
42709  * Bootstrap MoneyField class
42710  * 
42711  * @constructor
42712  * Create a new MoneyField.
42713  * @param {Object} config Configuration options
42714  */
42715
42716 Roo.bootstrap.MoneyField = function(config) {
42717     
42718     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42719     
42720 };
42721
42722 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42723     
42724     /**
42725      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42726      */
42727     allowDecimals : true,
42728     /**
42729      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42730      */
42731     decimalSeparator : ".",
42732     /**
42733      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42734      */
42735     decimalPrecision : 0,
42736     /**
42737      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42738      */
42739     allowNegative : true,
42740     /**
42741      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42742      */
42743     allowZero: true,
42744     /**
42745      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42746      */
42747     minValue : Number.NEGATIVE_INFINITY,
42748     /**
42749      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42750      */
42751     maxValue : Number.MAX_VALUE,
42752     /**
42753      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42754      */
42755     minText : "The minimum value for this field is {0}",
42756     /**
42757      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42758      */
42759     maxText : "The maximum value for this field is {0}",
42760     /**
42761      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42762      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42763      */
42764     nanText : "{0} is not a valid number",
42765     /**
42766      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42767      */
42768     castInt : true,
42769     /**
42770      * @cfg {String} defaults currency of the MoneyField
42771      * value should be in lkey
42772      */
42773     defaultCurrency : false,
42774     /**
42775      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42776      */
42777     thousandsDelimiter : false,
42778     /**
42779      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42780      */
42781     max_length: false,
42782     
42783     inputlg : 9,
42784     inputmd : 9,
42785     inputsm : 9,
42786     inputxs : 6,
42787     
42788     store : false,
42789     
42790     getAutoCreate : function()
42791     {
42792         var align = this.labelAlign || this.parentLabelAlign();
42793         
42794         var id = Roo.id();
42795
42796         var cfg = {
42797             cls: 'form-group',
42798             cn: []
42799         };
42800
42801         var input =  {
42802             tag: 'input',
42803             id : id,
42804             cls : 'form-control roo-money-amount-input',
42805             autocomplete: 'new-password'
42806         };
42807         
42808         var hiddenInput = {
42809             tag: 'input',
42810             type: 'hidden',
42811             id: Roo.id(),
42812             cls: 'hidden-number-input'
42813         };
42814         
42815         if(this.max_length) {
42816             input.maxlength = this.max_length; 
42817         }
42818         
42819         if (this.name) {
42820             hiddenInput.name = this.name;
42821         }
42822
42823         if (this.disabled) {
42824             input.disabled = true;
42825         }
42826
42827         var clg = 12 - this.inputlg;
42828         var cmd = 12 - this.inputmd;
42829         var csm = 12 - this.inputsm;
42830         var cxs = 12 - this.inputxs;
42831         
42832         var container = {
42833             tag : 'div',
42834             cls : 'row roo-money-field',
42835             cn : [
42836                 {
42837                     tag : 'div',
42838                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42839                     cn : [
42840                         {
42841                             tag : 'div',
42842                             cls: 'roo-select2-container input-group',
42843                             cn: [
42844                                 {
42845                                     tag : 'input',
42846                                     cls : 'form-control roo-money-currency-input',
42847                                     autocomplete: 'new-password',
42848                                     readOnly : 1,
42849                                     name : this.currencyName
42850                                 },
42851                                 {
42852                                     tag :'span',
42853                                     cls : 'input-group-addon',
42854                                     cn : [
42855                                         {
42856                                             tag: 'span',
42857                                             cls: 'caret'
42858                                         }
42859                                     ]
42860                                 }
42861                             ]
42862                         }
42863                     ]
42864                 },
42865                 {
42866                     tag : 'div',
42867                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42868                     cn : [
42869                         {
42870                             tag: 'div',
42871                             cls: this.hasFeedback ? 'has-feedback' : '',
42872                             cn: [
42873                                 input
42874                             ]
42875                         }
42876                     ]
42877                 }
42878             ]
42879             
42880         };
42881         
42882         if (this.fieldLabel.length) {
42883             var indicator = {
42884                 tag: 'i',
42885                 tooltip: 'This field is required'
42886             };
42887
42888             var label = {
42889                 tag: 'label',
42890                 'for':  id,
42891                 cls: 'control-label',
42892                 cn: []
42893             };
42894
42895             var label_text = {
42896                 tag: 'span',
42897                 html: this.fieldLabel
42898             };
42899
42900             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42901             label.cn = [
42902                 indicator,
42903                 label_text
42904             ];
42905
42906             if(this.indicatorpos == 'right') {
42907                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42908                 label.cn = [
42909                     label_text,
42910                     indicator
42911                 ];
42912             }
42913
42914             if(align == 'left') {
42915                 container = {
42916                     tag: 'div',
42917                     cn: [
42918                         container
42919                     ]
42920                 };
42921
42922                 if(this.labelWidth > 12){
42923                     label.style = "width: " + this.labelWidth + 'px';
42924                 }
42925                 if(this.labelWidth < 13 && this.labelmd == 0){
42926                     this.labelmd = this.labelWidth;
42927                 }
42928                 if(this.labellg > 0){
42929                     label.cls += ' col-lg-' + this.labellg;
42930                     input.cls += ' col-lg-' + (12 - this.labellg);
42931                 }
42932                 if(this.labelmd > 0){
42933                     label.cls += ' col-md-' + this.labelmd;
42934                     container.cls += ' col-md-' + (12 - this.labelmd);
42935                 }
42936                 if(this.labelsm > 0){
42937                     label.cls += ' col-sm-' + this.labelsm;
42938                     container.cls += ' col-sm-' + (12 - this.labelsm);
42939                 }
42940                 if(this.labelxs > 0){
42941                     label.cls += ' col-xs-' + this.labelxs;
42942                     container.cls += ' col-xs-' + (12 - this.labelxs);
42943                 }
42944             }
42945         }
42946
42947         cfg.cn = [
42948             label,
42949             container,
42950             hiddenInput
42951         ];
42952         
42953         var settings = this;
42954
42955         ['xs','sm','md','lg'].map(function(size){
42956             if (settings[size]) {
42957                 cfg.cls += ' col-' + size + '-' + settings[size];
42958             }
42959         });
42960         
42961         return cfg;
42962     },
42963     
42964     initEvents : function()
42965     {
42966         this.indicator = this.indicatorEl();
42967         
42968         this.initCurrencyEvent();
42969         
42970         this.initNumberEvent();
42971     },
42972     
42973     initCurrencyEvent : function()
42974     {
42975         if (!this.store) {
42976             throw "can not find store for combo";
42977         }
42978         
42979         this.store = Roo.factory(this.store, Roo.data);
42980         this.store.parent = this;
42981         
42982         this.createList();
42983         
42984         this.triggerEl = this.el.select('.input-group-addon', true).first();
42985         
42986         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42987         
42988         var _this = this;
42989         
42990         (function(){
42991             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42992             _this.list.setWidth(lw);
42993         }).defer(100);
42994         
42995         this.list.on('mouseover', this.onViewOver, this);
42996         this.list.on('mousemove', this.onViewMove, this);
42997         this.list.on('scroll', this.onViewScroll, this);
42998         
42999         if(!this.tpl){
43000             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43001         }
43002         
43003         this.view = new Roo.View(this.list, this.tpl, {
43004             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43005         });
43006         
43007         this.view.on('click', this.onViewClick, this);
43008         
43009         this.store.on('beforeload', this.onBeforeLoad, this);
43010         this.store.on('load', this.onLoad, this);
43011         this.store.on('loadexception', this.onLoadException, this);
43012         
43013         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43014             "up" : function(e){
43015                 this.inKeyMode = true;
43016                 this.selectPrev();
43017             },
43018
43019             "down" : function(e){
43020                 if(!this.isExpanded()){
43021                     this.onTriggerClick();
43022                 }else{
43023                     this.inKeyMode = true;
43024                     this.selectNext();
43025                 }
43026             },
43027
43028             "enter" : function(e){
43029                 this.collapse();
43030                 
43031                 if(this.fireEvent("specialkey", this, e)){
43032                     this.onViewClick(false);
43033                 }
43034                 
43035                 return true;
43036             },
43037
43038             "esc" : function(e){
43039                 this.collapse();
43040             },
43041
43042             "tab" : function(e){
43043                 this.collapse();
43044                 
43045                 if(this.fireEvent("specialkey", this, e)){
43046                     this.onViewClick(false);
43047                 }
43048                 
43049                 return true;
43050             },
43051
43052             scope : this,
43053
43054             doRelay : function(foo, bar, hname){
43055                 if(hname == 'down' || this.scope.isExpanded()){
43056                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43057                 }
43058                 return true;
43059             },
43060
43061             forceKeyDown: true
43062         });
43063         
43064         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43065         
43066     },
43067     
43068     initNumberEvent : function(e)
43069     {
43070         this.inputEl().on("keydown" , this.fireKey,  this);
43071         this.inputEl().on("focus", this.onFocus,  this);
43072         this.inputEl().on("blur", this.onBlur,  this);
43073         
43074         this.inputEl().relayEvent('keyup', this);
43075         
43076         if(this.indicator){
43077             this.indicator.addClass('invisible');
43078         }
43079  
43080         this.originalValue = this.getValue();
43081         
43082         if(this.validationEvent == 'keyup'){
43083             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43084             this.inputEl().on('keyup', this.filterValidation, this);
43085         }
43086         else if(this.validationEvent !== false){
43087             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43088         }
43089         
43090         if(this.selectOnFocus){
43091             this.on("focus", this.preFocus, this);
43092             
43093         }
43094         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43095             this.inputEl().on("keypress", this.filterKeys, this);
43096         } else {
43097             this.inputEl().relayEvent('keypress', this);
43098         }
43099         
43100         var allowed = "0123456789";
43101         
43102         if(this.allowDecimals){
43103             allowed += this.decimalSeparator;
43104         }
43105         
43106         if(this.allowNegative){
43107             allowed += "-";
43108         }
43109         
43110         if(this.thousandsDelimiter) {
43111             allowed += ",";
43112         }
43113         
43114         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43115         
43116         var keyPress = function(e){
43117             
43118             var k = e.getKey();
43119             
43120             var c = e.getCharCode();
43121             
43122             if(
43123                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43124                     allowed.indexOf(String.fromCharCode(c)) === -1
43125             ){
43126                 e.stopEvent();
43127                 return;
43128             }
43129             
43130             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43131                 return;
43132             }
43133             
43134             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43135                 e.stopEvent();
43136             }
43137         };
43138         
43139         this.inputEl().on("keypress", keyPress, this);
43140         
43141     },
43142     
43143     onTriggerClick : function(e)
43144     {   
43145         if(this.disabled){
43146             return;
43147         }
43148         
43149         this.page = 0;
43150         this.loadNext = false;
43151         
43152         if(this.isExpanded()){
43153             this.collapse();
43154             return;
43155         }
43156         
43157         this.hasFocus = true;
43158         
43159         if(this.triggerAction == 'all') {
43160             this.doQuery(this.allQuery, true);
43161             return;
43162         }
43163         
43164         this.doQuery(this.getRawValue());
43165     },
43166     
43167     getCurrency : function()
43168     {   
43169         var v = this.currencyEl().getValue();
43170         
43171         return v;
43172     },
43173     
43174     restrictHeight : function()
43175     {
43176         this.list.alignTo(this.currencyEl(), this.listAlign);
43177         this.list.alignTo(this.currencyEl(), this.listAlign);
43178     },
43179     
43180     onViewClick : function(view, doFocus, el, e)
43181     {
43182         var index = this.view.getSelectedIndexes()[0];
43183         
43184         var r = this.store.getAt(index);
43185         
43186         if(r){
43187             this.onSelect(r, index);
43188         }
43189     },
43190     
43191     onSelect : function(record, index){
43192         
43193         if(this.fireEvent('beforeselect', this, record, index) !== false){
43194         
43195             this.setFromCurrencyData(index > -1 ? record.data : false);
43196             
43197             this.collapse();
43198             
43199             this.fireEvent('select', this, record, index);
43200         }
43201     },
43202     
43203     setFromCurrencyData : function(o)
43204     {
43205         var currency = '';
43206         
43207         this.lastCurrency = o;
43208         
43209         if (this.currencyField) {
43210             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43211         } else {
43212             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43213         }
43214         
43215         this.lastSelectionText = currency;
43216         
43217         //setting default currency
43218         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43219             this.setCurrency(this.defaultCurrency);
43220             return;
43221         }
43222         
43223         this.setCurrency(currency);
43224     },
43225     
43226     setFromData : function(o)
43227     {
43228         var c = {};
43229         
43230         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43231         
43232         this.setFromCurrencyData(c);
43233         
43234         var value = '';
43235         
43236         if (this.name) {
43237             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43238         } else {
43239             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43240         }
43241         
43242         this.setValue(value);
43243         
43244     },
43245     
43246     setCurrency : function(v)
43247     {   
43248         this.currencyValue = v;
43249         
43250         if(this.rendered){
43251             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43252             this.validate();
43253         }
43254     },
43255     
43256     setValue : function(v)
43257     {
43258         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43259         
43260         this.value = v;
43261         
43262         if(this.rendered){
43263             
43264             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43265             
43266             this.inputEl().dom.value = (v == '') ? '' :
43267                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43268             
43269             if(!this.allowZero && v === '0') {
43270                 this.hiddenEl().dom.value = '';
43271                 this.inputEl().dom.value = '';
43272             }
43273             
43274             this.validate();
43275         }
43276     },
43277     
43278     getRawValue : function()
43279     {
43280         var v = this.inputEl().getValue();
43281         
43282         return v;
43283     },
43284     
43285     getValue : function()
43286     {
43287         return this.fixPrecision(this.parseValue(this.getRawValue()));
43288     },
43289     
43290     parseValue : function(value)
43291     {
43292         if(this.thousandsDelimiter) {
43293             value += "";
43294             r = new RegExp(",", "g");
43295             value = value.replace(r, "");
43296         }
43297         
43298         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43299         return isNaN(value) ? '' : value;
43300         
43301     },
43302     
43303     fixPrecision : function(value)
43304     {
43305         if(this.thousandsDelimiter) {
43306             value += "";
43307             r = new RegExp(",", "g");
43308             value = value.replace(r, "");
43309         }
43310         
43311         var nan = isNaN(value);
43312         
43313         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43314             return nan ? '' : value;
43315         }
43316         return parseFloat(value).toFixed(this.decimalPrecision);
43317     },
43318     
43319     decimalPrecisionFcn : function(v)
43320     {
43321         return Math.floor(v);
43322     },
43323     
43324     validateValue : function(value)
43325     {
43326         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43327             return false;
43328         }
43329         
43330         var num = this.parseValue(value);
43331         
43332         if(isNaN(num)){
43333             this.markInvalid(String.format(this.nanText, value));
43334             return false;
43335         }
43336         
43337         if(num < this.minValue){
43338             this.markInvalid(String.format(this.minText, this.minValue));
43339             return false;
43340         }
43341         
43342         if(num > this.maxValue){
43343             this.markInvalid(String.format(this.maxText, this.maxValue));
43344             return false;
43345         }
43346         
43347         return true;
43348     },
43349     
43350     validate : function()
43351     {
43352         if(this.disabled || this.allowBlank){
43353             this.markValid();
43354             return true;
43355         }
43356         
43357         var currency = this.getCurrency();
43358         
43359         if(this.validateValue(this.getRawValue()) && currency.length){
43360             this.markValid();
43361             return true;
43362         }
43363         
43364         this.markInvalid();
43365         return false;
43366     },
43367     
43368     getName: function()
43369     {
43370         return this.name;
43371     },
43372     
43373     beforeBlur : function()
43374     {
43375         if(!this.castInt){
43376             return;
43377         }
43378         
43379         var v = this.parseValue(this.getRawValue());
43380         
43381         if(v || v == 0){
43382             this.setValue(v);
43383         }
43384     },
43385     
43386     onBlur : function()
43387     {
43388         this.beforeBlur();
43389         
43390         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43391             //this.el.removeClass(this.focusClass);
43392         }
43393         
43394         this.hasFocus = false;
43395         
43396         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43397             this.validate();
43398         }
43399         
43400         var v = this.getValue();
43401         
43402         if(String(v) !== String(this.startValue)){
43403             this.fireEvent('change', this, v, this.startValue);
43404         }
43405         
43406         this.fireEvent("blur", this);
43407     },
43408     
43409     inputEl : function()
43410     {
43411         return this.el.select('.roo-money-amount-input', true).first();
43412     },
43413     
43414     currencyEl : function()
43415     {
43416         return this.el.select('.roo-money-currency-input', true).first();
43417     },
43418     
43419     hiddenEl : function()
43420     {
43421         return this.el.select('input.hidden-number-input',true).first();
43422     }
43423     
43424 });/**
43425  * @class Roo.bootstrap.BezierSignature
43426  * @extends Roo.bootstrap.Component
43427  * Bootstrap BezierSignature class
43428  * This script refer to:
43429  *    Title: Signature Pad
43430  *    Author: szimek
43431  *    Availability: https://github.com/szimek/signature_pad
43432  *
43433  * @constructor
43434  * Create a new BezierSignature
43435  * @param {Object} config The config object
43436  */
43437
43438 Roo.bootstrap.BezierSignature = function(config){
43439     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43440     this.addEvents({
43441         "resize" : true
43442     });
43443 };
43444
43445 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43446 {
43447      
43448     curve_data: [],
43449     
43450     is_empty: true,
43451     
43452     mouse_btn_down: true,
43453     
43454     /**
43455      * @cfg {int} canvas height
43456      */
43457     canvas_height: '200px',
43458     
43459     /**
43460      * @cfg {float|function} Radius of a single dot.
43461      */ 
43462     dot_size: false,
43463     
43464     /**
43465      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43466      */
43467     min_width: 0.5,
43468     
43469     /**
43470      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43471      */
43472     max_width: 2.5,
43473     
43474     /**
43475      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43476      */
43477     throttle: 16,
43478     
43479     /**
43480      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43481      */
43482     min_distance: 5,
43483     
43484     /**
43485      * @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.
43486      */
43487     bg_color: 'rgba(0, 0, 0, 0)',
43488     
43489     /**
43490      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43491      */
43492     dot_color: 'black',
43493     
43494     /**
43495      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43496      */ 
43497     velocity_filter_weight: 0.7,
43498     
43499     /**
43500      * @cfg {function} Callback when stroke begin. 
43501      */
43502     onBegin: false,
43503     
43504     /**
43505      * @cfg {function} Callback when stroke end.
43506      */
43507     onEnd: false,
43508     
43509     getAutoCreate : function()
43510     {
43511         var cls = 'roo-signature column';
43512         
43513         if(this.cls){
43514             cls += ' ' + this.cls;
43515         }
43516         
43517         var col_sizes = [
43518             'lg',
43519             'md',
43520             'sm',
43521             'xs'
43522         ];
43523         
43524         for(var i = 0; i < col_sizes.length; i++) {
43525             if(this[col_sizes[i]]) {
43526                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43527             }
43528         }
43529         
43530         var cfg = {
43531             tag: 'div',
43532             cls: cls,
43533             cn: [
43534                 {
43535                     tag: 'div',
43536                     cls: 'roo-signature-body',
43537                     cn: [
43538                         {
43539                             tag: 'canvas',
43540                             cls: 'roo-signature-body-canvas',
43541                             height: this.canvas_height,
43542                             width: this.canvas_width
43543                         }
43544                     ]
43545                 },
43546                 {
43547                     tag: 'input',
43548                     type: 'file',
43549                     style: 'display: none'
43550                 }
43551             ]
43552         };
43553         
43554         return cfg;
43555     },
43556     
43557     initEvents: function() 
43558     {
43559         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43560         
43561         var canvas = this.canvasEl();
43562         
43563         // mouse && touch event swapping...
43564         canvas.dom.style.touchAction = 'none';
43565         canvas.dom.style.msTouchAction = 'none';
43566         
43567         this.mouse_btn_down = false;
43568         canvas.on('mousedown', this._handleMouseDown, this);
43569         canvas.on('mousemove', this._handleMouseMove, this);
43570         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43571         
43572         if (window.PointerEvent) {
43573             canvas.on('pointerdown', this._handleMouseDown, this);
43574             canvas.on('pointermove', this._handleMouseMove, this);
43575             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43576         }
43577         
43578         if ('ontouchstart' in window) {
43579             canvas.on('touchstart', this._handleTouchStart, this);
43580             canvas.on('touchmove', this._handleTouchMove, this);
43581             canvas.on('touchend', this._handleTouchEnd, this);
43582         }
43583         
43584         Roo.EventManager.onWindowResize(this.resize, this, true);
43585         
43586         // file input event
43587         this.fileEl().on('change', this.uploadImage, this);
43588         
43589         this.clear();
43590         
43591         this.resize();
43592     },
43593     
43594     resize: function(){
43595         
43596         var canvas = this.canvasEl().dom;
43597         var ctx = this.canvasElCtx();
43598         var img_data = false;
43599         
43600         if(canvas.width > 0) {
43601             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43602         }
43603         // setting canvas width will clean img data
43604         canvas.width = 0;
43605         
43606         var style = window.getComputedStyle ? 
43607             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43608             
43609         var padding_left = parseInt(style.paddingLeft) || 0;
43610         var padding_right = parseInt(style.paddingRight) || 0;
43611         
43612         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43613         
43614         if(img_data) {
43615             ctx.putImageData(img_data, 0, 0);
43616         }
43617     },
43618     
43619     _handleMouseDown: function(e)
43620     {
43621         if (e.browserEvent.which === 1) {
43622             this.mouse_btn_down = true;
43623             this.strokeBegin(e);
43624         }
43625     },
43626     
43627     _handleMouseMove: function (e)
43628     {
43629         if (this.mouse_btn_down) {
43630             this.strokeMoveUpdate(e);
43631         }
43632     },
43633     
43634     _handleMouseUp: function (e)
43635     {
43636         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43637             this.mouse_btn_down = false;
43638             this.strokeEnd(e);
43639         }
43640     },
43641     
43642     _handleTouchStart: function (e) {
43643         
43644         e.preventDefault();
43645         if (e.browserEvent.targetTouches.length === 1) {
43646             // var touch = e.browserEvent.changedTouches[0];
43647             // this.strokeBegin(touch);
43648             
43649              this.strokeBegin(e); // assume e catching the correct xy...
43650         }
43651     },
43652     
43653     _handleTouchMove: function (e) {
43654         e.preventDefault();
43655         // var touch = event.targetTouches[0];
43656         // _this._strokeMoveUpdate(touch);
43657         this.strokeMoveUpdate(e);
43658     },
43659     
43660     _handleTouchEnd: function (e) {
43661         var wasCanvasTouched = e.target === this.canvasEl().dom;
43662         if (wasCanvasTouched) {
43663             e.preventDefault();
43664             // var touch = event.changedTouches[0];
43665             // _this._strokeEnd(touch);
43666             this.strokeEnd(e);
43667         }
43668     },
43669     
43670     reset: function () {
43671         this._lastPoints = [];
43672         this._lastVelocity = 0;
43673         this._lastWidth = (this.min_width + this.max_width) / 2;
43674         this.canvasElCtx().fillStyle = this.dot_color;
43675     },
43676     
43677     strokeMoveUpdate: function(e)
43678     {
43679         this.strokeUpdate(e);
43680         
43681         if (this.throttle) {
43682             this.throttleStroke(this.strokeUpdate, this.throttle);
43683         }
43684         else {
43685             this.strokeUpdate(e);
43686         }
43687     },
43688     
43689     strokeBegin: function(e)
43690     {
43691         var newPointGroup = {
43692             color: this.dot_color,
43693             points: []
43694         };
43695         
43696         if (typeof this.onBegin === 'function') {
43697             this.onBegin(e);
43698         }
43699         
43700         this.curve_data.push(newPointGroup);
43701         this.reset();
43702         this.strokeUpdate(e);
43703     },
43704     
43705     strokeUpdate: function(e)
43706     {
43707         var rect = this.canvasEl().dom.getBoundingClientRect();
43708         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43709         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43710         var lastPoints = lastPointGroup.points;
43711         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43712         var isLastPointTooClose = lastPoint
43713             ? point.distanceTo(lastPoint) <= this.min_distance
43714             : false;
43715         var color = lastPointGroup.color;
43716         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43717             var curve = this.addPoint(point);
43718             if (!lastPoint) {
43719                 this.drawDot({color: color, point: point});
43720             }
43721             else if (curve) {
43722                 this.drawCurve({color: color, curve: curve});
43723             }
43724             lastPoints.push({
43725                 time: point.time,
43726                 x: point.x,
43727                 y: point.y
43728             });
43729         }
43730     },
43731     
43732     strokeEnd: function(e)
43733     {
43734         this.strokeUpdate(e);
43735         if (typeof this.onEnd === 'function') {
43736             this.onEnd(e);
43737         }
43738     },
43739     
43740     addPoint:  function (point) {
43741         var _lastPoints = this._lastPoints;
43742         _lastPoints.push(point);
43743         if (_lastPoints.length > 2) {
43744             if (_lastPoints.length === 3) {
43745                 _lastPoints.unshift(_lastPoints[0]);
43746             }
43747             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43748             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43749             _lastPoints.shift();
43750             return curve;
43751         }
43752         return null;
43753     },
43754     
43755     calculateCurveWidths: function (startPoint, endPoint) {
43756         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43757             (1 - this.velocity_filter_weight) * this._lastVelocity;
43758
43759         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43760         var widths = {
43761             end: newWidth,
43762             start: this._lastWidth
43763         };
43764         
43765         this._lastVelocity = velocity;
43766         this._lastWidth = newWidth;
43767         return widths;
43768     },
43769     
43770     drawDot: function (_a) {
43771         var color = _a.color, point = _a.point;
43772         var ctx = this.canvasElCtx();
43773         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43774         ctx.beginPath();
43775         this.drawCurveSegment(point.x, point.y, width);
43776         ctx.closePath();
43777         ctx.fillStyle = color;
43778         ctx.fill();
43779     },
43780     
43781     drawCurve: function (_a) {
43782         var color = _a.color, curve = _a.curve;
43783         var ctx = this.canvasElCtx();
43784         var widthDelta = curve.endWidth - curve.startWidth;
43785         var drawSteps = Math.floor(curve.length()) * 2;
43786         ctx.beginPath();
43787         ctx.fillStyle = color;
43788         for (var i = 0; i < drawSteps; i += 1) {
43789         var t = i / drawSteps;
43790         var tt = t * t;
43791         var ttt = tt * t;
43792         var u = 1 - t;
43793         var uu = u * u;
43794         var uuu = uu * u;
43795         var x = uuu * curve.startPoint.x;
43796         x += 3 * uu * t * curve.control1.x;
43797         x += 3 * u * tt * curve.control2.x;
43798         x += ttt * curve.endPoint.x;
43799         var y = uuu * curve.startPoint.y;
43800         y += 3 * uu * t * curve.control1.y;
43801         y += 3 * u * tt * curve.control2.y;
43802         y += ttt * curve.endPoint.y;
43803         var width = curve.startWidth + ttt * widthDelta;
43804         this.drawCurveSegment(x, y, width);
43805         }
43806         ctx.closePath();
43807         ctx.fill();
43808     },
43809     
43810     drawCurveSegment: function (x, y, width) {
43811         var ctx = this.canvasElCtx();
43812         ctx.moveTo(x, y);
43813         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43814         this.is_empty = false;
43815     },
43816     
43817     clear: function()
43818     {
43819         var ctx = this.canvasElCtx();
43820         var canvas = this.canvasEl().dom;
43821         ctx.fillStyle = this.bg_color;
43822         ctx.clearRect(0, 0, canvas.width, canvas.height);
43823         ctx.fillRect(0, 0, canvas.width, canvas.height);
43824         this.curve_data = [];
43825         this.reset();
43826         this.is_empty = true;
43827     },
43828     
43829     fileEl: function()
43830     {
43831         return  this.el.select('input',true).first();
43832     },
43833     
43834     canvasEl: function()
43835     {
43836         return this.el.select('canvas',true).first();
43837     },
43838     
43839     canvasElCtx: function()
43840     {
43841         return this.el.select('canvas',true).first().dom.getContext('2d');
43842     },
43843     
43844     getImage: function(type)
43845     {
43846         if(this.is_empty) {
43847             return false;
43848         }
43849         
43850         // encryption ?
43851         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43852     },
43853     
43854     drawFromImage: function(img_src)
43855     {
43856         var img = new Image();
43857         
43858         img.onload = function(){
43859             this.canvasElCtx().drawImage(img, 0, 0);
43860         }.bind(this);
43861         
43862         img.src = img_src;
43863         
43864         this.is_empty = false;
43865     },
43866     
43867     selectImage: function()
43868     {
43869         this.fileEl().dom.click();
43870     },
43871     
43872     uploadImage: function(e)
43873     {
43874         var reader = new FileReader();
43875         
43876         reader.onload = function(e){
43877             var img = new Image();
43878             img.onload = function(){
43879                 this.reset();
43880                 this.canvasElCtx().drawImage(img, 0, 0);
43881             }.bind(this);
43882             img.src = e.target.result;
43883         }.bind(this);
43884         
43885         reader.readAsDataURL(e.target.files[0]);
43886     },
43887     
43888     // Bezier Point Constructor
43889     Point: (function () {
43890         function Point(x, y, time) {
43891             this.x = x;
43892             this.y = y;
43893             this.time = time || Date.now();
43894         }
43895         Point.prototype.distanceTo = function (start) {
43896             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43897         };
43898         Point.prototype.equals = function (other) {
43899             return this.x === other.x && this.y === other.y && this.time === other.time;
43900         };
43901         Point.prototype.velocityFrom = function (start) {
43902             return this.time !== start.time
43903             ? this.distanceTo(start) / (this.time - start.time)
43904             : 0;
43905         };
43906         return Point;
43907     }()),
43908     
43909     
43910     // Bezier Constructor
43911     Bezier: (function () {
43912         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43913             this.startPoint = startPoint;
43914             this.control2 = control2;
43915             this.control1 = control1;
43916             this.endPoint = endPoint;
43917             this.startWidth = startWidth;
43918             this.endWidth = endWidth;
43919         }
43920         Bezier.fromPoints = function (points, widths, scope) {
43921             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43922             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43923             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43924         };
43925         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43926             var dx1 = s1.x - s2.x;
43927             var dy1 = s1.y - s2.y;
43928             var dx2 = s2.x - s3.x;
43929             var dy2 = s2.y - s3.y;
43930             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43931             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43932             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43933             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43934             var dxm = m1.x - m2.x;
43935             var dym = m1.y - m2.y;
43936             var k = l2 / (l1 + l2);
43937             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43938             var tx = s2.x - cm.x;
43939             var ty = s2.y - cm.y;
43940             return {
43941                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43942                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43943             };
43944         };
43945         Bezier.prototype.length = function () {
43946             var steps = 10;
43947             var length = 0;
43948             var px;
43949             var py;
43950             for (var i = 0; i <= steps; i += 1) {
43951                 var t = i / steps;
43952                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43953                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43954                 if (i > 0) {
43955                     var xdiff = cx - px;
43956                     var ydiff = cy - py;
43957                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43958                 }
43959                 px = cx;
43960                 py = cy;
43961             }
43962             return length;
43963         };
43964         Bezier.prototype.point = function (t, start, c1, c2, end) {
43965             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43966             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43967             + (3.0 * c2 * (1.0 - t) * t * t)
43968             + (end * t * t * t);
43969         };
43970         return Bezier;
43971     }()),
43972     
43973     throttleStroke: function(fn, wait) {
43974       if (wait === void 0) { wait = 250; }
43975       var previous = 0;
43976       var timeout = null;
43977       var result;
43978       var storedContext;
43979       var storedArgs;
43980       var later = function () {
43981           previous = Date.now();
43982           timeout = null;
43983           result = fn.apply(storedContext, storedArgs);
43984           if (!timeout) {
43985               storedContext = null;
43986               storedArgs = [];
43987           }
43988       };
43989       return function wrapper() {
43990           var args = [];
43991           for (var _i = 0; _i < arguments.length; _i++) {
43992               args[_i] = arguments[_i];
43993           }
43994           var now = Date.now();
43995           var remaining = wait - (now - previous);
43996           storedContext = this;
43997           storedArgs = args;
43998           if (remaining <= 0 || remaining > wait) {
43999               if (timeout) {
44000                   clearTimeout(timeout);
44001                   timeout = null;
44002               }
44003               previous = now;
44004               result = fn.apply(storedContext, storedArgs);
44005               if (!timeout) {
44006                   storedContext = null;
44007                   storedArgs = [];
44008               }
44009           }
44010           else if (!timeout) {
44011               timeout = window.setTimeout(later, remaining);
44012           }
44013           return result;
44014       };
44015   }
44016   
44017 });
44018
44019  
44020
44021