Roo/bootstrap/Popover.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['display' + (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         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3951  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3952  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3953  * @cfg {String} size (sm|lg|xl) default empty
3954  * @cfg {Number} max_width set the max width of modal
3955  * @cfg {Boolean} editableTitle can the title be edited
3956
3957  *
3958  *
3959  * @constructor
3960  * Create a new Modal Dialog
3961  * @param {Object} config The config object
3962  */
3963
3964 Roo.bootstrap.Modal = function(config){
3965     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3966     this.addEvents({
3967         // raw events
3968         /**
3969          * @event btnclick
3970          * The raw btnclick event for the button
3971          * @param {Roo.EventObject} e
3972          */
3973         "btnclick" : true,
3974         /**
3975          * @event resize
3976          * Fire when dialog resize
3977          * @param {Roo.bootstrap.Modal} this
3978          * @param {Roo.EventObject} e
3979          */
3980         "resize" : true,
3981         /**
3982          * @event titlechanged
3983          * Fire when the editable title has been changed
3984          * @param {Roo.bootstrap.Modal} this
3985          * @param {Roo.EventObject} value
3986          */
3987         "titlechanged" : true 
3988         
3989     });
3990     this.buttons = this.buttons || [];
3991
3992     if (this.tmpl) {
3993         this.tmpl = Roo.factory(this.tmpl);
3994     }
3995
3996 };
3997
3998 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3999
4000     title : 'test dialog',
4001
4002     buttons : false,
4003
4004     // set on load...
4005
4006     html: false,
4007
4008     tmp: false,
4009
4010     specificTitle: false,
4011
4012     buttonPosition: 'right',
4013
4014     allow_close : true,
4015
4016     animate : true,
4017
4018     fitwindow: false,
4019     
4020      // private
4021     dialogEl: false,
4022     bodyEl:  false,
4023     footerEl:  false,
4024     titleEl:  false,
4025     closeEl:  false,
4026
4027     size: '',
4028     
4029     max_width: 0,
4030     
4031     max_height: 0,
4032     
4033     fit_content: false,
4034     editableTitle  : false,
4035
4036     onRender : function(ct, position)
4037     {
4038         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4039
4040         if(!this.el){
4041             var cfg = Roo.apply({},  this.getAutoCreate());
4042             cfg.id = Roo.id();
4043             //if(!cfg.name){
4044             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4045             //}
4046             //if (!cfg.name.length) {
4047             //    delete cfg.name;
4048            // }
4049             if (this.cls) {
4050                 cfg.cls += ' ' + this.cls;
4051             }
4052             if (this.style) {
4053                 cfg.style = this.style;
4054             }
4055             this.el = Roo.get(document.body).createChild(cfg, position);
4056         }
4057         //var type = this.el.dom.type;
4058
4059
4060         if(this.tabIndex !== undefined){
4061             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4062         }
4063
4064         this.dialogEl = this.el.select('.modal-dialog',true).first();
4065         this.bodyEl = this.el.select('.modal-body',true).first();
4066         this.closeEl = this.el.select('.modal-header .close', true).first();
4067         this.headerEl = this.el.select('.modal-header',true).first();
4068         this.titleEl = this.el.select('.modal-title',true).first();
4069         this.footerEl = this.el.select('.modal-footer',true).first();
4070
4071         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4072         
4073         //this.el.addClass("x-dlg-modal");
4074
4075         if (this.buttons.length) {
4076             Roo.each(this.buttons, function(bb) {
4077                 var b = Roo.apply({}, bb);
4078                 b.xns = b.xns || Roo.bootstrap;
4079                 b.xtype = b.xtype || 'Button';
4080                 if (typeof(b.listeners) == 'undefined') {
4081                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4082                 }
4083
4084                 var btn = Roo.factory(b);
4085
4086                 btn.render(this.getButtonContainer());
4087
4088             },this);
4089         }
4090         // render the children.
4091         var nitems = [];
4092
4093         if(typeof(this.items) != 'undefined'){
4094             var items = this.items;
4095             delete this.items;
4096
4097             for(var i =0;i < items.length;i++) {
4098                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4099             }
4100         }
4101
4102         this.items = nitems;
4103
4104         // where are these used - they used to be body/close/footer
4105
4106
4107         this.initEvents();
4108         //this.el.addClass([this.fieldClass, this.cls]);
4109
4110     },
4111
4112     getAutoCreate : function()
4113     {
4114         // we will default to modal-body-overflow - might need to remove or make optional later.
4115         var bdy = {
4116                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4117                 html : this.html || ''
4118         };
4119
4120         var title = {
4121             tag: 'h5',
4122             cls : 'modal-title',
4123             html : this.title
4124         };
4125
4126         if(this.specificTitle){ // WTF is this?
4127             title = this.title;
4128         }
4129
4130         var header = [];
4131         if (this.allow_close && Roo.bootstrap.version == 3) {
4132             header.push({
4133                 tag: 'button',
4134                 cls : 'close',
4135                 html : '&times'
4136             });
4137         }
4138
4139         header.push(title);
4140
4141         if (this.editableTitle) {
4142             header.push({
4143                 cls: 'form-control roo-editable-title d-none',
4144                 tag: 'input',
4145                 type: 'text'
4146             });
4147         }
4148         
4149         if (this.allow_close && Roo.bootstrap.version == 4) {
4150             header.push({
4151                 tag: 'button',
4152                 cls : 'close',
4153                 html : '&times'
4154             });
4155         }
4156         
4157         var size = '';
4158
4159         if(this.size.length){
4160             size = 'modal-' + this.size;
4161         }
4162         
4163         var footer = Roo.bootstrap.version == 3 ?
4164             {
4165                 cls : 'modal-footer',
4166                 cn : [
4167                     {
4168                         tag: 'div',
4169                         cls: 'btn-' + this.buttonPosition
4170                     }
4171                 ]
4172
4173             } :
4174             {  // BS4 uses mr-auto on left buttons....
4175                 cls : 'modal-footer'
4176             };
4177
4178             
4179
4180         
4181         
4182         var modal = {
4183             cls: "modal",
4184              cn : [
4185                 {
4186                     cls: "modal-dialog " + size,
4187                     cn : [
4188                         {
4189                             cls : "modal-content",
4190                             cn : [
4191                                 {
4192                                     cls : 'modal-header',
4193                                     cn : header
4194                                 },
4195                                 bdy,
4196                                 footer
4197                             ]
4198
4199                         }
4200                     ]
4201
4202                 }
4203             ]
4204         };
4205
4206         if(this.animate){
4207             modal.cls += ' fade';
4208         }
4209
4210         return modal;
4211
4212     },
4213     getChildContainer : function() {
4214
4215          return this.bodyEl;
4216
4217     },
4218     getButtonContainer : function() {
4219         
4220          return Roo.bootstrap.version == 4 ?
4221             this.el.select('.modal-footer',true).first()
4222             : this.el.select('.modal-footer div',true).first();
4223
4224     },
4225     initEvents : function()
4226     {
4227         if (this.allow_close) {
4228             this.closeEl.on('click', this.hide, this);
4229         }
4230         Roo.EventManager.onWindowResize(this.resize, this, true);
4231         if (this.editableTitle) {
4232             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4233             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4234             this.headerEditEl.on('keyup', function(e) {
4235                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4236                         this.toggleHeaderInput(false)
4237                     }
4238                 }, this);
4239             this.headerEditEl.on('blur', function(e) {
4240                 this.toggleHeaderInput(false)
4241             },this);
4242         }
4243
4244     },
4245   
4246
4247     resize : function()
4248     {
4249         this.maskEl.setSize(
4250             Roo.lib.Dom.getViewWidth(true),
4251             Roo.lib.Dom.getViewHeight(true)
4252         );
4253         
4254         if (this.fitwindow) {
4255             
4256            this.dialogEl.setStyle( { 'max-width' : '100%' });
4257             this.setSize(
4258                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4259                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4260             );
4261             return;
4262         }
4263         
4264         if(this.max_width !== 0) {
4265             
4266             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4267             
4268             if(this.height) {
4269                 this.setSize(w, this.height);
4270                 return;
4271             }
4272             
4273             if(this.max_height) {
4274                 this.setSize(w,Math.min(
4275                     this.max_height,
4276                     Roo.lib.Dom.getViewportHeight(true) - 60
4277                 ));
4278                 
4279                 return;
4280             }
4281             
4282             if(!this.fit_content) {
4283                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4284                 return;
4285             }
4286             
4287             this.setSize(w, Math.min(
4288                 60 +
4289                 this.headerEl.getHeight() + 
4290                 this.footerEl.getHeight() + 
4291                 this.getChildHeight(this.bodyEl.dom.childNodes),
4292                 Roo.lib.Dom.getViewportHeight(true) - 60)
4293             );
4294         }
4295         
4296     },
4297
4298     setSize : function(w,h)
4299     {
4300         if (!w && !h) {
4301             return;
4302         }
4303         
4304         this.resizeTo(w,h);
4305     },
4306
4307     show : function() {
4308
4309         if (!this.rendered) {
4310             this.render();
4311         }
4312         this.toggleHeaderInput(false);
4313         //this.el.setStyle('display', 'block');
4314         this.el.removeClass('hideing');
4315         this.el.dom.style.display='block';
4316         
4317         Roo.get(document.body).addClass('modal-open');
4318  
4319         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4320             
4321             (function(){
4322                 this.el.addClass('show');
4323                 this.el.addClass('in');
4324             }).defer(50, this);
4325         }else{
4326             this.el.addClass('show');
4327             this.el.addClass('in');
4328         }
4329
4330         // not sure how we can show data in here..
4331         //if (this.tmpl) {
4332         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4333         //}
4334
4335         Roo.get(document.body).addClass("x-body-masked");
4336         
4337         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4338         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4339         this.maskEl.dom.style.display = 'block';
4340         this.maskEl.addClass('show');
4341         
4342         
4343         this.resize();
4344         
4345         this.fireEvent('show', this);
4346
4347         // set zindex here - otherwise it appears to be ignored...
4348         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4349
4350         (function () {
4351             this.items.forEach( function(e) {
4352                 e.layout ? e.layout() : false;
4353
4354             });
4355         }).defer(100,this);
4356
4357     },
4358     hide : function()
4359     {
4360         if(this.fireEvent("beforehide", this) !== false){
4361             
4362             this.maskEl.removeClass('show');
4363             
4364             this.maskEl.dom.style.display = '';
4365             Roo.get(document.body).removeClass("x-body-masked");
4366             this.el.removeClass('in');
4367             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4368
4369             if(this.animate){ // why
4370                 this.el.addClass('hideing');
4371                 this.el.removeClass('show');
4372                 (function(){
4373                     if (!this.el.hasClass('hideing')) {
4374                         return; // it's been shown again...
4375                     }
4376                     
4377                     this.el.dom.style.display='';
4378
4379                     Roo.get(document.body).removeClass('modal-open');
4380                     this.el.removeClass('hideing');
4381                 }).defer(150,this);
4382                 
4383             }else{
4384                 this.el.removeClass('show');
4385                 this.el.dom.style.display='';
4386                 Roo.get(document.body).removeClass('modal-open');
4387
4388             }
4389             this.fireEvent('hide', this);
4390         }
4391     },
4392     isVisible : function()
4393     {
4394         
4395         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4396         
4397     },
4398
4399     addButton : function(str, cb)
4400     {
4401
4402
4403         var b = Roo.apply({}, { html : str } );
4404         b.xns = b.xns || Roo.bootstrap;
4405         b.xtype = b.xtype || 'Button';
4406         if (typeof(b.listeners) == 'undefined') {
4407             b.listeners = { click : cb.createDelegate(this)  };
4408         }
4409
4410         var btn = Roo.factory(b);
4411
4412         btn.render(this.getButtonContainer());
4413
4414         return btn;
4415
4416     },
4417
4418     setDefaultButton : function(btn)
4419     {
4420         //this.el.select('.modal-footer').()
4421     },
4422
4423     resizeTo: function(w,h)
4424     {
4425         this.dialogEl.setWidth(w);
4426         
4427         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4428
4429         this.bodyEl.setHeight(h - diff);
4430         
4431         this.fireEvent('resize', this);
4432     },
4433     
4434     setContentSize  : function(w, h)
4435     {
4436
4437     },
4438     onButtonClick: function(btn,e)
4439     {
4440         //Roo.log([a,b,c]);
4441         this.fireEvent('btnclick', btn.name, e);
4442     },
4443      /**
4444      * Set the title of the Dialog
4445      * @param {String} str new Title
4446      */
4447     setTitle: function(str) {
4448         this.titleEl.dom.innerHTML = str;
4449         this.title = str;
4450     },
4451     /**
4452      * Set the body of the Dialog
4453      * @param {String} str new Title
4454      */
4455     setBody: function(str) {
4456         this.bodyEl.dom.innerHTML = str;
4457     },
4458     /**
4459      * Set the body of the Dialog using the template
4460      * @param {Obj} data - apply this data to the template and replace the body contents.
4461      */
4462     applyBody: function(obj)
4463     {
4464         if (!this.tmpl) {
4465             Roo.log("Error - using apply Body without a template");
4466             //code
4467         }
4468         this.tmpl.overwrite(this.bodyEl, obj);
4469     },
4470     
4471     getChildHeight : function(child_nodes)
4472     {
4473         if(
4474             !child_nodes ||
4475             child_nodes.length == 0
4476         ) {
4477             return 0;
4478         }
4479         
4480         var child_height = 0;
4481         
4482         for(var i = 0; i < child_nodes.length; i++) {
4483             
4484             /*
4485             * for modal with tabs...
4486             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4487                 
4488                 var layout_childs = child_nodes[i].childNodes;
4489                 
4490                 for(var j = 0; j < layout_childs.length; j++) {
4491                     
4492                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4493                         
4494                         var layout_body_childs = layout_childs[j].childNodes;
4495                         
4496                         for(var k = 0; k < layout_body_childs.length; k++) {
4497                             
4498                             if(layout_body_childs[k].classList.contains('navbar')) {
4499                                 child_height += layout_body_childs[k].offsetHeight;
4500                                 continue;
4501                             }
4502                             
4503                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4504                                 
4505                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4506                                 
4507                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4508                                     
4509                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4510                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4511                                         continue;
4512                                     }
4513                                     
4514                                 }
4515                                 
4516                             }
4517                             
4518                         }
4519                     }
4520                 }
4521                 continue;
4522             }
4523             */
4524             
4525             child_height += child_nodes[i].offsetHeight;
4526             // Roo.log(child_nodes[i].offsetHeight);
4527         }
4528         
4529         return child_height;
4530     },
4531     toggleHeaderInput : function(is_edit)
4532     {
4533         if (!this.editableTitle) {
4534             return; // not editable.
4535         }
4536         if (is_edit && this.is_header_editing) {
4537             return; // already editing..
4538         }
4539         if (is_edit) {
4540     
4541             this.headerEditEl.dom.value = this.title;
4542             this.headerEditEl.removeClass('d-none');
4543             this.headerEditEl.dom.focus();
4544             this.titleEl.addClass('d-none');
4545             
4546             this.is_header_editing = true;
4547             return
4548         }
4549         // flip back to not editing.
4550         this.title = this.headerEditEl.dom.value;
4551         this.headerEditEl.addClass('d-none');
4552         this.titleEl.removeClass('d-none');
4553         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4554         this.is_header_editing = false;
4555         this.fireEvent('titlechanged', this, this.title);
4556     
4557             
4558         
4559     }
4560
4561 });
4562
4563
4564 Roo.apply(Roo.bootstrap.Modal,  {
4565     /**
4566          * Button config that displays a single OK button
4567          * @type Object
4568          */
4569         OK :  [{
4570             name : 'ok',
4571             weight : 'primary',
4572             html : 'OK'
4573         }],
4574         /**
4575          * Button config that displays Yes and No buttons
4576          * @type Object
4577          */
4578         YESNO : [
4579             {
4580                 name  : 'no',
4581                 html : 'No'
4582             },
4583             {
4584                 name  :'yes',
4585                 weight : 'primary',
4586                 html : 'Yes'
4587             }
4588         ],
4589
4590         /**
4591          * Button config that displays OK and Cancel buttons
4592          * @type Object
4593          */
4594         OKCANCEL : [
4595             {
4596                name : 'cancel',
4597                 html : 'Cancel'
4598             },
4599             {
4600                 name : 'ok',
4601                 weight : 'primary',
4602                 html : 'OK'
4603             }
4604         ],
4605         /**
4606          * Button config that displays Yes, No and Cancel buttons
4607          * @type Object
4608          */
4609         YESNOCANCEL : [
4610             {
4611                 name : 'yes',
4612                 weight : 'primary',
4613                 html : 'Yes'
4614             },
4615             {
4616                 name : 'no',
4617                 html : 'No'
4618             },
4619             {
4620                 name : 'cancel',
4621                 html : 'Cancel'
4622             }
4623         ],
4624         
4625         zIndex : 10001
4626 });
4627
4628 /*
4629  * - LGPL
4630  *
4631  * messagebox - can be used as a replace
4632  * 
4633  */
4634 /**
4635  * @class Roo.MessageBox
4636  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4637  * Example usage:
4638  *<pre><code>
4639 // Basic alert:
4640 Roo.Msg.alert('Status', 'Changes saved successfully.');
4641
4642 // Prompt for user data:
4643 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4644     if (btn == 'ok'){
4645         // process text value...
4646     }
4647 });
4648
4649 // Show a dialog using config options:
4650 Roo.Msg.show({
4651    title:'Save Changes?',
4652    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4653    buttons: Roo.Msg.YESNOCANCEL,
4654    fn: processResult,
4655    animEl: 'elId'
4656 });
4657 </code></pre>
4658  * @singleton
4659  */
4660 Roo.bootstrap.MessageBox = function(){
4661     var dlg, opt, mask, waitTimer;
4662     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4663     var buttons, activeTextEl, bwidth;
4664
4665     
4666     // private
4667     var handleButton = function(button){
4668         dlg.hide();
4669         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4670     };
4671
4672     // private
4673     var handleHide = function(){
4674         if(opt && opt.cls){
4675             dlg.el.removeClass(opt.cls);
4676         }
4677         //if(waitTimer){
4678         //    Roo.TaskMgr.stop(waitTimer);
4679         //    waitTimer = null;
4680         //}
4681     };
4682
4683     // private
4684     var updateButtons = function(b){
4685         var width = 0;
4686         if(!b){
4687             buttons["ok"].hide();
4688             buttons["cancel"].hide();
4689             buttons["yes"].hide();
4690             buttons["no"].hide();
4691             dlg.footerEl.hide();
4692             
4693             return width;
4694         }
4695         dlg.footerEl.show();
4696         for(var k in buttons){
4697             if(typeof buttons[k] != "function"){
4698                 if(b[k]){
4699                     buttons[k].show();
4700                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4701                     width += buttons[k].el.getWidth()+15;
4702                 }else{
4703                     buttons[k].hide();
4704                 }
4705             }
4706         }
4707         return width;
4708     };
4709
4710     // private
4711     var handleEsc = function(d, k, e){
4712         if(opt && opt.closable !== false){
4713             dlg.hide();
4714         }
4715         if(e){
4716             e.stopEvent();
4717         }
4718     };
4719
4720     return {
4721         /**
4722          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4723          * @return {Roo.BasicDialog} The BasicDialog element
4724          */
4725         getDialog : function(){
4726            if(!dlg){
4727                 dlg = new Roo.bootstrap.Modal( {
4728                     //draggable: true,
4729                     //resizable:false,
4730                     //constraintoviewport:false,
4731                     //fixedcenter:true,
4732                     //collapsible : false,
4733                     //shim:true,
4734                     //modal: true,
4735                 //    width: 'auto',
4736                   //  height:100,
4737                     //buttonAlign:"center",
4738                     closeClick : function(){
4739                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4740                             handleButton("no");
4741                         }else{
4742                             handleButton("cancel");
4743                         }
4744                     }
4745                 });
4746                 dlg.render();
4747                 dlg.on("hide", handleHide);
4748                 mask = dlg.mask;
4749                 //dlg.addKeyListener(27, handleEsc);
4750                 buttons = {};
4751                 this.buttons = buttons;
4752                 var bt = this.buttonText;
4753                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4754                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4755                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4756                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4757                 //Roo.log(buttons);
4758                 bodyEl = dlg.bodyEl.createChild({
4759
4760                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4761                         '<textarea class="roo-mb-textarea"></textarea>' +
4762                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4763                 });
4764                 msgEl = bodyEl.dom.firstChild;
4765                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4766                 textboxEl.enableDisplayMode();
4767                 textboxEl.addKeyListener([10,13], function(){
4768                     if(dlg.isVisible() && opt && opt.buttons){
4769                         if(opt.buttons.ok){
4770                             handleButton("ok");
4771                         }else if(opt.buttons.yes){
4772                             handleButton("yes");
4773                         }
4774                     }
4775                 });
4776                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4777                 textareaEl.enableDisplayMode();
4778                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4779                 progressEl.enableDisplayMode();
4780                 
4781                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4782                 var pf = progressEl.dom.firstChild;
4783                 if (pf) {
4784                     pp = Roo.get(pf.firstChild);
4785                     pp.setHeight(pf.offsetHeight);
4786                 }
4787                 
4788             }
4789             return dlg;
4790         },
4791
4792         /**
4793          * Updates the message box body text
4794          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4795          * the XHTML-compliant non-breaking space character '&amp;#160;')
4796          * @return {Roo.MessageBox} This message box
4797          */
4798         updateText : function(text)
4799         {
4800             if(!dlg.isVisible() && !opt.width){
4801                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4802                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4803             }
4804             msgEl.innerHTML = text || '&#160;';
4805       
4806             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4807             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4808             var w = Math.max(
4809                     Math.min(opt.width || cw , this.maxWidth), 
4810                     Math.max(opt.minWidth || this.minWidth, bwidth)
4811             );
4812             if(opt.prompt){
4813                 activeTextEl.setWidth(w);
4814             }
4815             if(dlg.isVisible()){
4816                 dlg.fixedcenter = false;
4817             }
4818             // to big, make it scroll. = But as usual stupid IE does not support
4819             // !important..
4820             
4821             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4822                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4823                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.height = '';
4826                 bodyEl.dom.style.overflowY = '';
4827             }
4828             if (cw > w) {
4829                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4830             } else {
4831                 bodyEl.dom.style.overflowX = '';
4832             }
4833             
4834             dlg.setContentSize(w, bodyEl.getHeight());
4835             if(dlg.isVisible()){
4836                 dlg.fixedcenter = true;
4837             }
4838             return this;
4839         },
4840
4841         /**
4842          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4843          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4844          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4845          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4846          * @return {Roo.MessageBox} This message box
4847          */
4848         updateProgress : function(value, text){
4849             if(text){
4850                 this.updateText(text);
4851             }
4852             
4853             if (pp) { // weird bug on my firefox - for some reason this is not defined
4854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4855                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4856             }
4857             return this;
4858         },        
4859
4860         /**
4861          * Returns true if the message box is currently displayed
4862          * @return {Boolean} True if the message box is visible, else false
4863          */
4864         isVisible : function(){
4865             return dlg && dlg.isVisible();  
4866         },
4867
4868         /**
4869          * Hides the message box if it is displayed
4870          */
4871         hide : function(){
4872             if(this.isVisible()){
4873                 dlg.hide();
4874             }  
4875         },
4876
4877         /**
4878          * Displays a new message box, or reinitializes an existing message box, based on the config options
4879          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4880          * The following config object properties are supported:
4881          * <pre>
4882 Property    Type             Description
4883 ----------  ---------------  ------------------------------------------------------------------------------------
4884 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4885                                    closes (defaults to undefined)
4886 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4887                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4888 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4889                                    progress and wait dialogs will ignore this property and always hide the
4890                                    close button as they can only be closed programmatically.
4891 cls               String           A custom CSS class to apply to the message box element
4892 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4893                                    displayed (defaults to 75)
4894 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4895                                    function will be btn (the name of the button that was clicked, if applicable,
4896                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4897                                    Progress and wait dialogs will ignore this option since they do not respond to
4898                                    user actions and can only be closed programmatically, so any required function
4899                                    should be called by the same code after it closes the dialog.
4900 icon              String           A CSS class that provides a background image to be used as an icon for
4901                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4902 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4903 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4904 modal             Boolean          False to allow user interaction with the page while the message box is
4905                                    displayed (defaults to true)
4906 msg               String           A string that will replace the existing message box body text (defaults
4907                                    to the XHTML-compliant non-breaking space character '&#160;')
4908 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4909 progress          Boolean          True to display a progress bar (defaults to false)
4910 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4911 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4912 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4913 title             String           The title text
4914 value             String           The string value to set into the active textbox element if displayed
4915 wait              Boolean          True to display a progress bar (defaults to false)
4916 width             Number           The width of the dialog in pixels
4917 </pre>
4918          *
4919          * Example usage:
4920          * <pre><code>
4921 Roo.Msg.show({
4922    title: 'Address',
4923    msg: 'Please enter your address:',
4924    width: 300,
4925    buttons: Roo.MessageBox.OKCANCEL,
4926    multiline: true,
4927    fn: saveAddress,
4928    animEl: 'addAddressBtn'
4929 });
4930 </code></pre>
4931          * @param {Object} config Configuration options
4932          * @return {Roo.MessageBox} This message box
4933          */
4934         show : function(options)
4935         {
4936             
4937             // this causes nightmares if you show one dialog after another
4938             // especially on callbacks..
4939              
4940             if(this.isVisible()){
4941                 
4942                 this.hide();
4943                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4944                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4945                 Roo.log("New Dialog Message:" +  options.msg )
4946                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4947                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4948                 
4949             }
4950             var d = this.getDialog();
4951             opt = options;
4952             d.setTitle(opt.title || "&#160;");
4953             d.closeEl.setDisplayed(opt.closable !== false);
4954             activeTextEl = textboxEl;
4955             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4956             if(opt.prompt){
4957                 if(opt.multiline){
4958                     textboxEl.hide();
4959                     textareaEl.show();
4960                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4961                         opt.multiline : this.defaultTextHeight);
4962                     activeTextEl = textareaEl;
4963                 }else{
4964                     textboxEl.show();
4965                     textareaEl.hide();
4966                 }
4967             }else{
4968                 textboxEl.hide();
4969                 textareaEl.hide();
4970             }
4971             progressEl.setDisplayed(opt.progress === true);
4972             if (opt.progress) {
4973                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4974             }
4975             this.updateProgress(0);
4976             activeTextEl.dom.value = opt.value || "";
4977             if(opt.prompt){
4978                 dlg.setDefaultButton(activeTextEl);
4979             }else{
4980                 var bs = opt.buttons;
4981                 var db = null;
4982                 if(bs && bs.ok){
4983                     db = buttons["ok"];
4984                 }else if(bs && bs.yes){
4985                     db = buttons["yes"];
4986                 }
4987                 dlg.setDefaultButton(db);
4988             }
4989             bwidth = updateButtons(opt.buttons);
4990             this.updateText(opt.msg);
4991             if(opt.cls){
4992                 d.el.addClass(opt.cls);
4993             }
4994             d.proxyDrag = opt.proxyDrag === true;
4995             d.modal = opt.modal !== false;
4996             d.mask = opt.modal !== false ? mask : false;
4997             if(!d.isVisible()){
4998                 // force it to the end of the z-index stack so it gets a cursor in FF
4999                 document.body.appendChild(dlg.el.dom);
5000                 d.animateTarget = null;
5001                 d.show(options.animEl);
5002             }
5003             return this;
5004         },
5005
5006         /**
5007          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5008          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5009          * and closing the message box when the process is complete.
5010          * @param {String} title The title bar text
5011          * @param {String} msg The message box body text
5012          * @return {Roo.MessageBox} This message box
5013          */
5014         progress : function(title, msg){
5015             this.show({
5016                 title : title,
5017                 msg : msg,
5018                 buttons: false,
5019                 progress:true,
5020                 closable:false,
5021                 minWidth: this.minProgressWidth,
5022                 modal : true
5023             });
5024             return this;
5025         },
5026
5027         /**
5028          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5029          * If a callback function is passed it will be called after the user clicks the button, and the
5030          * id of the button that was clicked will be passed as the only parameter to the callback
5031          * (could also be the top-right close button).
5032          * @param {String} title The title bar text
5033          * @param {String} msg The message box body text
5034          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5035          * @param {Object} scope (optional) The scope of the callback function
5036          * @return {Roo.MessageBox} This message box
5037          */
5038         alert : function(title, msg, fn, scope)
5039         {
5040             this.show({
5041                 title : title,
5042                 msg : msg,
5043                 buttons: this.OK,
5044                 fn: fn,
5045                 closable : false,
5046                 scope : scope,
5047                 modal : true
5048             });
5049             return this;
5050         },
5051
5052         /**
5053          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5054          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5055          * You are responsible for closing the message box when the process is complete.
5056          * @param {String} msg The message box body text
5057          * @param {String} title (optional) The title bar text
5058          * @return {Roo.MessageBox} This message box
5059          */
5060         wait : function(msg, title){
5061             this.show({
5062                 title : title,
5063                 msg : msg,
5064                 buttons: false,
5065                 closable:false,
5066                 progress:true,
5067                 modal:true,
5068                 width:300,
5069                 wait:true
5070             });
5071             waitTimer = Roo.TaskMgr.start({
5072                 run: function(i){
5073                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5074                 },
5075                 interval: 1000
5076             });
5077             return this;
5078         },
5079
5080         /**
5081          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5082          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5083          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5084          * @param {String} title The title bar text
5085          * @param {String} msg The message box body text
5086          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5087          * @param {Object} scope (optional) The scope of the callback function
5088          * @return {Roo.MessageBox} This message box
5089          */
5090         confirm : function(title, msg, fn, scope){
5091             this.show({
5092                 title : title,
5093                 msg : msg,
5094                 buttons: this.YESNO,
5095                 fn: fn,
5096                 scope : scope,
5097                 modal : true
5098             });
5099             return this;
5100         },
5101
5102         /**
5103          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5104          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5105          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5106          * (could also be the top-right close button) and the text that was entered will be passed as the two
5107          * parameters to the callback.
5108          * @param {String} title The title bar text
5109          * @param {String} msg The message box body text
5110          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111          * @param {Object} scope (optional) The scope of the callback function
5112          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5113          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5114          * @return {Roo.MessageBox} This message box
5115          */
5116         prompt : function(title, msg, fn, scope, multiline){
5117             this.show({
5118                 title : title,
5119                 msg : msg,
5120                 buttons: this.OKCANCEL,
5121                 fn: fn,
5122                 minWidth:250,
5123                 scope : scope,
5124                 prompt:true,
5125                 multiline: multiline,
5126                 modal : true
5127             });
5128             return this;
5129         },
5130
5131         /**
5132          * Button config that displays a single OK button
5133          * @type Object
5134          */
5135         OK : {ok:true},
5136         /**
5137          * Button config that displays Yes and No buttons
5138          * @type Object
5139          */
5140         YESNO : {yes:true, no:true},
5141         /**
5142          * Button config that displays OK and Cancel buttons
5143          * @type Object
5144          */
5145         OKCANCEL : {ok:true, cancel:true},
5146         /**
5147          * Button config that displays Yes, No and Cancel buttons
5148          * @type Object
5149          */
5150         YESNOCANCEL : {yes:true, no:true, cancel:true},
5151
5152         /**
5153          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5154          * @type Number
5155          */
5156         defaultTextHeight : 75,
5157         /**
5158          * The maximum width in pixels of the message box (defaults to 600)
5159          * @type Number
5160          */
5161         maxWidth : 600,
5162         /**
5163          * The minimum width in pixels of the message box (defaults to 100)
5164          * @type Number
5165          */
5166         minWidth : 100,
5167         /**
5168          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5169          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5170          * @type Number
5171          */
5172         minProgressWidth : 250,
5173         /**
5174          * An object containing the default button text strings that can be overriden for localized language support.
5175          * Supported properties are: ok, cancel, yes and no.
5176          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5177          * @type Object
5178          */
5179         buttonText : {
5180             ok : "OK",
5181             cancel : "Cancel",
5182             yes : "Yes",
5183             no : "No"
5184         }
5185     };
5186 }();
5187
5188 /**
5189  * Shorthand for {@link Roo.MessageBox}
5190  */
5191 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5192 Roo.Msg = Roo.Msg || Roo.MessageBox;
5193 /*
5194  * - LGPL
5195  *
5196  * navbar
5197  * 
5198  */
5199
5200 /**
5201  * @class Roo.bootstrap.Navbar
5202  * @extends Roo.bootstrap.Component
5203  * Bootstrap Navbar class
5204
5205  * @constructor
5206  * Create a new Navbar
5207  * @param {Object} config The config object
5208  */
5209
5210
5211 Roo.bootstrap.Navbar = function(config){
5212     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5213     this.addEvents({
5214         // raw events
5215         /**
5216          * @event beforetoggle
5217          * Fire before toggle the menu
5218          * @param {Roo.EventObject} e
5219          */
5220         "beforetoggle" : true
5221     });
5222 };
5223
5224 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5225     
5226     
5227    
5228     // private
5229     navItems : false,
5230     loadMask : false,
5231     
5232     
5233     getAutoCreate : function(){
5234         
5235         
5236         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5237         
5238     },
5239     
5240     initEvents :function ()
5241     {
5242         //Roo.log(this.el.select('.navbar-toggle',true));
5243         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5244         
5245         var mark = {
5246             tag: "div",
5247             cls:"x-dlg-mask"
5248         };
5249         
5250         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5251         
5252         var size = this.el.getSize();
5253         this.maskEl.setSize(size.width, size.height);
5254         this.maskEl.enableDisplayMode("block");
5255         this.maskEl.hide();
5256         
5257         if(this.loadMask){
5258             this.maskEl.show();
5259         }
5260     },
5261     
5262     
5263     getChildContainer : function()
5264     {
5265         if (this.el && this.el.select('.collapse').getCount()) {
5266             return this.el.select('.collapse',true).first();
5267         }
5268         
5269         return this.el;
5270     },
5271     
5272     mask : function()
5273     {
5274         this.maskEl.show();
5275     },
5276     
5277     unmask : function()
5278     {
5279         this.maskEl.hide();
5280     },
5281     onToggle : function()
5282     {
5283         
5284         if(this.fireEvent('beforetoggle', this) === false){
5285             return;
5286         }
5287         var ce = this.el.select('.navbar-collapse',true).first();
5288       
5289         if (!ce.hasClass('show')) {
5290            this.expand();
5291         } else {
5292             this.collapse();
5293         }
5294         
5295         
5296     
5297     },
5298     /**
5299      * Expand the navbar pulldown 
5300      */
5301     expand : function ()
5302     {
5303        
5304         var ce = this.el.select('.navbar-collapse',true).first();
5305         if (ce.hasClass('collapsing')) {
5306             return;
5307         }
5308         ce.dom.style.height = '';
5309                // show it...
5310         ce.addClass('in'); // old...
5311         ce.removeClass('collapse');
5312         ce.addClass('show');
5313         var h = ce.getHeight();
5314         Roo.log(h);
5315         ce.removeClass('show');
5316         // at this point we should be able to see it..
5317         ce.addClass('collapsing');
5318         
5319         ce.setHeight(0); // resize it ...
5320         ce.on('transitionend', function() {
5321             //Roo.log('done transition');
5322             ce.removeClass('collapsing');
5323             ce.addClass('show');
5324             ce.removeClass('collapse');
5325
5326             ce.dom.style.height = '';
5327         }, this, { single: true} );
5328         ce.setHeight(h);
5329         ce.dom.scrollTop = 0;
5330     },
5331     /**
5332      * Collapse the navbar pulldown 
5333      */
5334     collapse : function()
5335     {
5336          var ce = this.el.select('.navbar-collapse',true).first();
5337        
5338         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5339             // it's collapsed or collapsing..
5340             return;
5341         }
5342         ce.removeClass('in'); // old...
5343         ce.setHeight(ce.getHeight());
5344         ce.removeClass('show');
5345         ce.addClass('collapsing');
5346         
5347         ce.on('transitionend', function() {
5348             ce.dom.style.height = '';
5349             ce.removeClass('collapsing');
5350             ce.addClass('collapse');
5351         }, this, { single: true} );
5352         ce.setHeight(0);
5353     }
5354     
5355     
5356     
5357 });
5358
5359
5360
5361  
5362
5363  /*
5364  * - LGPL
5365  *
5366  * navbar
5367  * 
5368  */
5369
5370 /**
5371  * @class Roo.bootstrap.NavSimplebar
5372  * @extends Roo.bootstrap.Navbar
5373  * Bootstrap Sidebar class
5374  *
5375  * @cfg {Boolean} inverse is inverted color
5376  * 
5377  * @cfg {String} type (nav | pills | tabs)
5378  * @cfg {Boolean} arrangement stacked | justified
5379  * @cfg {String} align (left | right) alignment
5380  * 
5381  * @cfg {Boolean} main (true|false) main nav bar? default false
5382  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5383  * 
5384  * @cfg {String} tag (header|footer|nav|div) default is nav 
5385
5386  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5387  * 
5388  * 
5389  * @constructor
5390  * Create a new Sidebar
5391  * @param {Object} config The config object
5392  */
5393
5394
5395 Roo.bootstrap.NavSimplebar = function(config){
5396     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5397 };
5398
5399 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5400     
5401     inverse: false,
5402     
5403     type: false,
5404     arrangement: '',
5405     align : false,
5406     
5407     weight : 'light',
5408     
5409     main : false,
5410     
5411     
5412     tag : false,
5413     
5414     
5415     getAutoCreate : function(){
5416         
5417         
5418         var cfg = {
5419             tag : this.tag || 'div',
5420             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5421         };
5422         if (['light','white'].indexOf(this.weight) > -1) {
5423             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5424         }
5425         cfg.cls += ' bg-' + this.weight;
5426         
5427         if (this.inverse) {
5428             cfg.cls += ' navbar-inverse';
5429             
5430         }
5431         
5432         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5433         
5434         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5435             return cfg;
5436         }
5437         
5438         
5439     
5440         
5441         cfg.cn = [
5442             {
5443                 cls: 'nav nav-' + this.xtype,
5444                 tag : 'ul'
5445             }
5446         ];
5447         
5448          
5449         this.type = this.type || 'nav';
5450         if (['tabs','pills'].indexOf(this.type) != -1) {
5451             cfg.cn[0].cls += ' nav-' + this.type
5452         
5453         
5454         } else {
5455             if (this.type!=='nav') {
5456                 Roo.log('nav type must be nav/tabs/pills')
5457             }
5458             cfg.cn[0].cls += ' navbar-nav'
5459         }
5460         
5461         
5462         
5463         
5464         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5465             cfg.cn[0].cls += ' nav-' + this.arrangement;
5466         }
5467         
5468         
5469         if (this.align === 'right') {
5470             cfg.cn[0].cls += ' navbar-right';
5471         }
5472         
5473         
5474         
5475         
5476         return cfg;
5477     
5478         
5479     }
5480     
5481     
5482     
5483 });
5484
5485
5486
5487  
5488
5489  
5490        /*
5491  * - LGPL
5492  *
5493  * navbar
5494  * navbar-fixed-top
5495  * navbar-expand-md  fixed-top 
5496  */
5497
5498 /**
5499  * @class Roo.bootstrap.NavHeaderbar
5500  * @extends Roo.bootstrap.NavSimplebar
5501  * Bootstrap Sidebar class
5502  *
5503  * @cfg {String} brand what is brand
5504  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5505  * @cfg {String} brand_href href of the brand
5506  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5507  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5508  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5509  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5510  * 
5511  * @constructor
5512  * Create a new Sidebar
5513  * @param {Object} config The config object
5514  */
5515
5516
5517 Roo.bootstrap.NavHeaderbar = function(config){
5518     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5519       
5520 };
5521
5522 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5523     
5524     position: '',
5525     brand: '',
5526     brand_href: false,
5527     srButton : true,
5528     autohide : false,
5529     desktopCenter : false,
5530    
5531     
5532     getAutoCreate : function(){
5533         
5534         var   cfg = {
5535             tag: this.nav || 'nav',
5536             cls: 'navbar navbar-expand-md',
5537             role: 'navigation',
5538             cn: []
5539         };
5540         
5541         var cn = cfg.cn;
5542         if (this.desktopCenter) {
5543             cn.push({cls : 'container', cn : []});
5544             cn = cn[0].cn;
5545         }
5546         
5547         if(this.srButton){
5548             var btn = {
5549                 tag: 'button',
5550                 type: 'button',
5551                 cls: 'navbar-toggle navbar-toggler',
5552                 'data-toggle': 'collapse',
5553                 cn: [
5554                     {
5555                         tag: 'span',
5556                         cls: 'sr-only',
5557                         html: 'Toggle navigation'
5558                     },
5559                     {
5560                         tag: 'span',
5561                         cls: 'icon-bar navbar-toggler-icon'
5562                     },
5563                     {
5564                         tag: 'span',
5565                         cls: 'icon-bar'
5566                     },
5567                     {
5568                         tag: 'span',
5569                         cls: 'icon-bar'
5570                     }
5571                 ]
5572             };
5573             
5574             cn.push( Roo.bootstrap.version == 4 ? btn : {
5575                 tag: 'div',
5576                 cls: 'navbar-header',
5577                 cn: [
5578                     btn
5579                 ]
5580             });
5581         }
5582         
5583         cn.push({
5584             tag: 'div',
5585             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5586             cn : []
5587         });
5588         
5589         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5590         
5591         if (['light','white'].indexOf(this.weight) > -1) {
5592             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5593         }
5594         cfg.cls += ' bg-' + this.weight;
5595         
5596         
5597         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5598             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5599             
5600             // tag can override this..
5601             
5602             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5603         }
5604         
5605         if (this.brand !== '') {
5606             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5607             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5608                 tag: 'a',
5609                 href: this.brand_href ? this.brand_href : '#',
5610                 cls: 'navbar-brand',
5611                 cn: [
5612                 this.brand
5613                 ]
5614             });
5615         }
5616         
5617         if(this.main){
5618             cfg.cls += ' main-nav';
5619         }
5620         
5621         
5622         return cfg;
5623
5624         
5625     },
5626     getHeaderChildContainer : function()
5627     {
5628         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5629             return this.el.select('.navbar-header',true).first();
5630         }
5631         
5632         return this.getChildContainer();
5633     },
5634     
5635     getChildContainer : function()
5636     {
5637          
5638         return this.el.select('.roo-navbar-collapse',true).first();
5639          
5640         
5641     },
5642     
5643     initEvents : function()
5644     {
5645         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5646         
5647         if (this.autohide) {
5648             
5649             var prevScroll = 0;
5650             var ft = this.el;
5651             
5652             Roo.get(document).on('scroll',function(e) {
5653                 var ns = Roo.get(document).getScroll().top;
5654                 var os = prevScroll;
5655                 prevScroll = ns;
5656                 
5657                 if(ns > os){
5658                     ft.removeClass('slideDown');
5659                     ft.addClass('slideUp');
5660                     return;
5661                 }
5662                 ft.removeClass('slideUp');
5663                 ft.addClass('slideDown');
5664                  
5665               
5666           },this);
5667         }
5668     }    
5669     
5670 });
5671
5672
5673
5674  
5675
5676  /*
5677  * - LGPL
5678  *
5679  * navbar
5680  * 
5681  */
5682
5683 /**
5684  * @class Roo.bootstrap.NavSidebar
5685  * @extends Roo.bootstrap.Navbar
5686  * Bootstrap Sidebar class
5687  * 
5688  * @constructor
5689  * Create a new Sidebar
5690  * @param {Object} config The config object
5691  */
5692
5693
5694 Roo.bootstrap.NavSidebar = function(config){
5695     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5696 };
5697
5698 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5699     
5700     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5701     
5702     getAutoCreate : function(){
5703         
5704         
5705         return  {
5706             tag: 'div',
5707             cls: 'sidebar sidebar-nav'
5708         };
5709     
5710         
5711     }
5712     
5713     
5714     
5715 });
5716
5717
5718
5719  
5720
5721  /*
5722  * - LGPL
5723  *
5724  * nav group
5725  * 
5726  */
5727
5728 /**
5729  * @class Roo.bootstrap.NavGroup
5730  * @extends Roo.bootstrap.Component
5731  * Bootstrap NavGroup class
5732  * @cfg {String} align (left|right)
5733  * @cfg {Boolean} inverse
5734  * @cfg {String} type (nav|pills|tab) default nav
5735  * @cfg {String} navId - reference Id for navbar.
5736  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5737  * 
5738  * @constructor
5739  * Create a new nav group
5740  * @param {Object} config The config object
5741  */
5742
5743 Roo.bootstrap.NavGroup = function(config){
5744     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5745     this.navItems = [];
5746    
5747     Roo.bootstrap.NavGroup.register(this);
5748      this.addEvents({
5749         /**
5750              * @event changed
5751              * Fires when the active item changes
5752              * @param {Roo.bootstrap.NavGroup} this
5753              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5754              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5755          */
5756         'changed': true
5757      });
5758     
5759 };
5760
5761 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5762     
5763     align: '',
5764     inverse: false,
5765     form: false,
5766     type: 'nav',
5767     navId : '',
5768     // private
5769     pilltype : true,
5770     
5771     navItems : false, 
5772     
5773     getAutoCreate : function()
5774     {
5775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5776         
5777         cfg = {
5778             tag : 'ul',
5779             cls: 'nav' 
5780         };
5781         if (Roo.bootstrap.version == 4) {
5782             if (['tabs','pills'].indexOf(this.type) != -1) {
5783                 cfg.cls += ' nav-' + this.type; 
5784             } else {
5785                 // trying to remove so header bar can right align top?
5786                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787                     // do not use on header bar... 
5788                     cfg.cls += ' navbar-nav';
5789                 }
5790             }
5791             
5792         } else {
5793             if (['tabs','pills'].indexOf(this.type) != -1) {
5794                 cfg.cls += ' nav-' + this.type
5795             } else {
5796                 if (this.type !== 'nav') {
5797                     Roo.log('nav type must be nav/tabs/pills')
5798                 }
5799                 cfg.cls += ' navbar-nav'
5800             }
5801         }
5802         
5803         if (this.parent() && this.parent().sidebar) {
5804             cfg = {
5805                 tag: 'ul',
5806                 cls: 'dashboard-menu sidebar-menu'
5807             };
5808             
5809             return cfg;
5810         }
5811         
5812         if (this.form === true) {
5813             cfg = {
5814                 tag: 'form',
5815                 cls: 'navbar-form form-inline'
5816             };
5817             //nav navbar-right ml-md-auto
5818             if (this.align === 'right') {
5819                 cfg.cls += ' navbar-right ml-md-auto';
5820             } else {
5821                 cfg.cls += ' navbar-left';
5822             }
5823         }
5824         
5825         if (this.align === 'right') {
5826             cfg.cls += ' navbar-right ml-md-auto';
5827         } else {
5828             cfg.cls += ' mr-auto';
5829         }
5830         
5831         if (this.inverse) {
5832             cfg.cls += ' navbar-inverse';
5833             
5834         }
5835         
5836         
5837         return cfg;
5838     },
5839     /**
5840     * sets the active Navigation item
5841     * @param {Roo.bootstrap.NavItem} the new current navitem
5842     */
5843     setActiveItem : function(item)
5844     {
5845         var prev = false;
5846         Roo.each(this.navItems, function(v){
5847             if (v == item) {
5848                 return ;
5849             }
5850             if (v.isActive()) {
5851                 v.setActive(false, true);
5852                 prev = v;
5853                 
5854             }
5855             
5856         });
5857
5858         item.setActive(true, true);
5859         this.fireEvent('changed', this, item, prev);
5860         
5861         
5862     },
5863     /**
5864     * gets the active Navigation item
5865     * @return {Roo.bootstrap.NavItem} the current navitem
5866     */
5867     getActive : function()
5868     {
5869         
5870         var prev = false;
5871         Roo.each(this.navItems, function(v){
5872             
5873             if (v.isActive()) {
5874                 prev = v;
5875                 
5876             }
5877             
5878         });
5879         return prev;
5880     },
5881     
5882     indexOfNav : function()
5883     {
5884         
5885         var prev = false;
5886         Roo.each(this.navItems, function(v,i){
5887             
5888             if (v.isActive()) {
5889                 prev = i;
5890                 
5891             }
5892             
5893         });
5894         return prev;
5895     },
5896     /**
5897     * adds a Navigation item
5898     * @param {Roo.bootstrap.NavItem} the navitem to add
5899     */
5900     addItem : function(cfg)
5901     {
5902         if (this.form && Roo.bootstrap.version == 4) {
5903             cfg.tag = 'div';
5904         }
5905         var cn = new Roo.bootstrap.NavItem(cfg);
5906         this.register(cn);
5907         cn.parentId = this.id;
5908         cn.onRender(this.el, null);
5909         return cn;
5910     },
5911     /**
5912     * register a Navigation item
5913     * @param {Roo.bootstrap.NavItem} the navitem to add
5914     */
5915     register : function(item)
5916     {
5917         this.navItems.push( item);
5918         item.navId = this.navId;
5919     
5920     },
5921     
5922     /**
5923     * clear all the Navigation item
5924     */
5925    
5926     clearAll : function()
5927     {
5928         this.navItems = [];
5929         this.el.dom.innerHTML = '';
5930     },
5931     
5932     getNavItem: function(tabId)
5933     {
5934         var ret = false;
5935         Roo.each(this.navItems, function(e) {
5936             if (e.tabId == tabId) {
5937                ret =  e;
5938                return false;
5939             }
5940             return true;
5941             
5942         });
5943         return ret;
5944     },
5945     
5946     setActiveNext : function()
5947     {
5948         var i = this.indexOfNav(this.getActive());
5949         if (i > this.navItems.length) {
5950             return;
5951         }
5952         this.setActiveItem(this.navItems[i+1]);
5953     },
5954     setActivePrev : function()
5955     {
5956         var i = this.indexOfNav(this.getActive());
5957         if (i  < 1) {
5958             return;
5959         }
5960         this.setActiveItem(this.navItems[i-1]);
5961     },
5962     clearWasActive : function(except) {
5963         Roo.each(this.navItems, function(e) {
5964             if (e.tabId != except.tabId && e.was_active) {
5965                e.was_active = false;
5966                return false;
5967             }
5968             return true;
5969             
5970         });
5971     },
5972     getWasActive : function ()
5973     {
5974         var r = false;
5975         Roo.each(this.navItems, function(e) {
5976             if (e.was_active) {
5977                r = e;
5978                return false;
5979             }
5980             return true;
5981             
5982         });
5983         return r;
5984     }
5985     
5986     
5987 });
5988
5989  
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5991     
5992     groups: {},
5993      /**
5994     * register a Navigation Group
5995     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5996     */
5997     register : function(navgrp)
5998     {
5999         this.groups[navgrp.navId] = navgrp;
6000         
6001     },
6002     /**
6003     * fetch a Navigation Group based on the navigation ID
6004     * @param {string} the navgroup to add
6005     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6006     */
6007     get: function(navId) {
6008         if (typeof(this.groups[navId]) == 'undefined') {
6009             return false;
6010             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6011         }
6012         return this.groups[navId] ;
6013     }
6014     
6015     
6016     
6017 });
6018
6019  /*
6020  * - LGPL
6021  *
6022  * row
6023  * 
6024  */
6025
6026 /**
6027  * @class Roo.bootstrap.NavItem
6028  * @extends Roo.bootstrap.Component
6029  * Bootstrap Navbar.NavItem class
6030  * @cfg {String} href  link to
6031  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6032  * @cfg {Boolean} button_outline show and outlined button
6033  * @cfg {String} html content of button
6034  * @cfg {String} badge text inside badge
6035  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036  * @cfg {String} glyphicon DEPRICATED - use fa
6037  * @cfg {String} icon DEPRICATED - use fa
6038  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039  * @cfg {Boolean} active Is item active
6040  * @cfg {Boolean} disabled Is item disabled
6041  * @cfg {String} linkcls  Link Class
6042  * @cfg {Boolean} preventDefault (true | false) default false
6043  * @cfg {String} tabId the tab that this item activates.
6044  * @cfg {String} tagtype (a|span) render as a href or span?
6045  * @cfg {Boolean} animateRef (true|false) link to element default false  
6046   
6047  * @constructor
6048  * Create a new Navbar Item
6049  * @param {Object} config The config object
6050  */
6051 Roo.bootstrap.NavItem = function(config){
6052     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6053     this.addEvents({
6054         // raw events
6055         /**
6056          * @event click
6057          * The raw click event for the entire grid.
6058          * @param {Roo.EventObject} e
6059          */
6060         "click" : true,
6061          /**
6062             * @event changed
6063             * Fires when the active item active state changes
6064             * @param {Roo.bootstrap.NavItem} this
6065             * @param {boolean} state the new state
6066              
6067          */
6068         'changed': true,
6069         /**
6070             * @event scrollto
6071             * Fires when scroll to element
6072             * @param {Roo.bootstrap.NavItem} this
6073             * @param {Object} options
6074             * @param {Roo.EventObject} e
6075              
6076          */
6077         'scrollto': true
6078     });
6079    
6080 };
6081
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6083     
6084     href: false,
6085     html: '',
6086     badge: '',
6087     icon: false,
6088     fa : false,
6089     glyphicon: false,
6090     active: false,
6091     preventDefault : false,
6092     tabId : false,
6093     tagtype : 'a',
6094     tag: 'li',
6095     disabled : false,
6096     animateRef : false,
6097     was_active : false,
6098     button_weight : '',
6099     button_outline : false,
6100     linkcls : '',
6101     navLink: false,
6102     
6103     getAutoCreate : function(){
6104          
6105         var cfg = {
6106             tag: this.tag,
6107             cls: 'nav-item'
6108         };
6109         
6110         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6111         
6112         if (this.active) {
6113             cfg.cls +=  ' active' ;
6114         }
6115         if (this.disabled) {
6116             cfg.cls += ' disabled';
6117         }
6118         
6119         // BS4 only?
6120         if (this.button_weight.length) {
6121             cfg.tag = this.href ? 'a' : 'button';
6122             cfg.html = this.html || '';
6123             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6124             if (this.href) {
6125                 cfg.href = this.href;
6126             }
6127             if (this.fa) {
6128                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6129             }
6130             
6131             // menu .. should add dropdown-menu class - so no need for carat..
6132             
6133             if (this.badge !== '') {
6134                  
6135                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6136             }
6137             return cfg;
6138         }
6139         
6140         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6141             cfg.cn = [
6142                 {
6143                     tag: this.tagtype,
6144                     href : this.href || "#",
6145                     html: this.html || ''
6146                 }
6147             ];
6148             if (this.tagtype == 'a') {
6149                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6150         
6151             }
6152             if (this.icon) {
6153                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6154             }
6155             if (this.fa) {
6156                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if(this.glyphicon) {
6159                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6160             }
6161             
6162             if (this.menu) {
6163                 
6164                 cfg.cn[0].html += " <span class='caret'></span>";
6165              
6166             }
6167             
6168             if (this.badge !== '') {
6169                  
6170                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6171             }
6172         }
6173         
6174         
6175         
6176         return cfg;
6177     },
6178     onRender : function(ct, position)
6179     {
6180        // Roo.log("Call onRender: " + this.xtype);
6181         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6182             this.tag = 'div';
6183         }
6184         
6185         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6186         this.navLink = this.el.select('.nav-link',true).first();
6187         return ret;
6188     },
6189       
6190     
6191     initEvents: function() 
6192     {
6193         if (typeof (this.menu) != 'undefined') {
6194             this.menu.parentType = this.xtype;
6195             this.menu.triggerEl = this.el;
6196             this.menu = this.addxtype(Roo.apply({}, this.menu));
6197         }
6198         
6199         this.el.on('click', this.onClick, this);
6200         
6201         //if(this.tagtype == 'span'){
6202         //    this.el.select('span',true).on('click', this.onClick, this);
6203         //}
6204        
6205         // at this point parent should be available..
6206         this.parent().register(this);
6207     },
6208     
6209     onClick : function(e)
6210     {
6211         if (e.getTarget('.dropdown-menu-item')) {
6212             // did you click on a menu itemm.... - then don't trigger onclick..
6213             return;
6214         }
6215         
6216         if(
6217                 this.preventDefault || 
6218                 this.href == '#' 
6219         ){
6220             Roo.log("NavItem - prevent Default?");
6221             e.preventDefault();
6222         }
6223         
6224         if (this.disabled) {
6225             return;
6226         }
6227         
6228         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6229         if (tg && tg.transition) {
6230             Roo.log("waiting for the transitionend");
6231             return;
6232         }
6233         
6234         
6235         
6236         //Roo.log("fire event clicked");
6237         if(this.fireEvent('click', this, e) === false){
6238             return;
6239         };
6240         
6241         if(this.tagtype == 'span'){
6242             return;
6243         }
6244         
6245         //Roo.log(this.href);
6246         var ael = this.el.select('a',true).first();
6247         //Roo.log(ael);
6248         
6249         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6250             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6251             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6252                 return; // ignore... - it's a 'hash' to another page.
6253             }
6254             Roo.log("NavItem - prevent Default?");
6255             e.preventDefault();
6256             this.scrollToElement(e);
6257         }
6258         
6259         
6260         var p =  this.parent();
6261    
6262         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6263             if (typeof(p.setActiveItem) !== 'undefined') {
6264                 p.setActiveItem(this);
6265             }
6266         }
6267         
6268         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6269         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6270             // remove the collapsed menu expand...
6271             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6272         }
6273     },
6274     
6275     isActive: function () {
6276         return this.active
6277     },
6278     setActive : function(state, fire, is_was_active)
6279     {
6280         if (this.active && !state && this.navId) {
6281             this.was_active = true;
6282             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6283             if (nv) {
6284                 nv.clearWasActive(this);
6285             }
6286             
6287         }
6288         this.active = state;
6289         
6290         if (!state ) {
6291             this.el.removeClass('active');
6292             this.navLink ? this.navLink.removeClass('active') : false;
6293         } else if (!this.el.hasClass('active')) {
6294             
6295             this.el.addClass('active');
6296             if (Roo.bootstrap.version == 4 && this.navLink ) {
6297                 this.navLink.addClass('active');
6298             }
6299             
6300         }
6301         if (fire) {
6302             this.fireEvent('changed', this, state);
6303         }
6304         
6305         // show a panel if it's registered and related..
6306         
6307         if (!this.navId || !this.tabId || !state || is_was_active) {
6308             return;
6309         }
6310         
6311         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6312         if (!tg) {
6313             return;
6314         }
6315         var pan = tg.getPanelByName(this.tabId);
6316         if (!pan) {
6317             return;
6318         }
6319         // if we can not flip to new panel - go back to old nav highlight..
6320         if (false == tg.showPanel(pan)) {
6321             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6322             if (nv) {
6323                 var onav = nv.getWasActive();
6324                 if (onav) {
6325                     onav.setActive(true, false, true);
6326                 }
6327             }
6328             
6329         }
6330         
6331         
6332         
6333     },
6334      // this should not be here...
6335     setDisabled : function(state)
6336     {
6337         this.disabled = state;
6338         if (!state ) {
6339             this.el.removeClass('disabled');
6340         } else if (!this.el.hasClass('disabled')) {
6341             this.el.addClass('disabled');
6342         }
6343         
6344     },
6345     
6346     /**
6347      * Fetch the element to display the tooltip on.
6348      * @return {Roo.Element} defaults to this.el
6349      */
6350     tooltipEl : function()
6351     {
6352         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6353     },
6354     
6355     scrollToElement : function(e)
6356     {
6357         var c = document.body;
6358         
6359         /*
6360          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6361          */
6362         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6363             c = document.documentElement;
6364         }
6365         
6366         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6367         
6368         if(!target){
6369             return;
6370         }
6371
6372         var o = target.calcOffsetsTo(c);
6373         
6374         var options = {
6375             target : target,
6376             value : o[1]
6377         };
6378         
6379         this.fireEvent('scrollto', this, options, e);
6380         
6381         Roo.get(c).scrollTo('top', options.value, true);
6382         
6383         return;
6384     }
6385 });
6386  
6387
6388  /*
6389  * - LGPL
6390  *
6391  * sidebar item
6392  *
6393  *  li
6394  *    <span> icon </span>
6395  *    <span> text </span>
6396  *    <span>badge </span>
6397  */
6398
6399 /**
6400  * @class Roo.bootstrap.NavSidebarItem
6401  * @extends Roo.bootstrap.NavItem
6402  * Bootstrap Navbar.NavSidebarItem class
6403  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6404  * {Boolean} open is the menu open
6405  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6406  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6407  * {String} buttonSize (sm|md|lg)the extra classes for the button
6408  * {Boolean} showArrow show arrow next to the text (default true)
6409  * @constructor
6410  * Create a new Navbar Button
6411  * @param {Object} config The config object
6412  */
6413 Roo.bootstrap.NavSidebarItem = function(config){
6414     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6415     this.addEvents({
6416         // raw events
6417         /**
6418          * @event click
6419          * The raw click event for the entire grid.
6420          * @param {Roo.EventObject} e
6421          */
6422         "click" : true,
6423          /**
6424             * @event changed
6425             * Fires when the active item active state changes
6426             * @param {Roo.bootstrap.NavSidebarItem} this
6427             * @param {boolean} state the new state
6428              
6429          */
6430         'changed': true
6431     });
6432    
6433 };
6434
6435 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6436     
6437     badgeWeight : 'default',
6438     
6439     open: false,
6440     
6441     buttonView : false,
6442     
6443     buttonWeight : 'default',
6444     
6445     buttonSize : 'md',
6446     
6447     showArrow : true,
6448     
6449     getAutoCreate : function(){
6450         
6451         
6452         var a = {
6453                 tag: 'a',
6454                 href : this.href || '#',
6455                 cls: '',
6456                 html : '',
6457                 cn : []
6458         };
6459         
6460         if(this.buttonView){
6461             a = {
6462                 tag: 'button',
6463                 href : this.href || '#',
6464                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6465                 html : this.html,
6466                 cn : []
6467             };
6468         }
6469         
6470         var cfg = {
6471             tag: 'li',
6472             cls: '',
6473             cn: [ a ]
6474         };
6475         
6476         if (this.active) {
6477             cfg.cls += ' active';
6478         }
6479         
6480         if (this.disabled) {
6481             cfg.cls += ' disabled';
6482         }
6483         if (this.open) {
6484             cfg.cls += ' open x-open';
6485         }
6486         // left icon..
6487         if (this.glyphicon || this.icon) {
6488             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6489             a.cn.push({ tag : 'i', cls : c }) ;
6490         }
6491         
6492         if(!this.buttonView){
6493             var span = {
6494                 tag: 'span',
6495                 html : this.html || ''
6496             };
6497
6498             a.cn.push(span);
6499             
6500         }
6501         
6502         if (this.badge !== '') {
6503             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6504         }
6505         
6506         if (this.menu) {
6507             
6508             if(this.showArrow){
6509                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6510             }
6511             
6512             a.cls += ' dropdown-toggle treeview' ;
6513         }
6514         
6515         return cfg;
6516     },
6517     
6518     initEvents : function()
6519     { 
6520         if (typeof (this.menu) != 'undefined') {
6521             this.menu.parentType = this.xtype;
6522             this.menu.triggerEl = this.el;
6523             this.menu = this.addxtype(Roo.apply({}, this.menu));
6524         }
6525         
6526         this.el.on('click', this.onClick, this);
6527         
6528         if(this.badge !== ''){
6529             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6530         }
6531         
6532     },
6533     
6534     onClick : function(e)
6535     {
6536         if(this.disabled){
6537             e.preventDefault();
6538             return;
6539         }
6540         
6541         if(this.preventDefault){
6542             e.preventDefault();
6543         }
6544         
6545         this.fireEvent('click', this, e);
6546     },
6547     
6548     disable : function()
6549     {
6550         this.setDisabled(true);
6551     },
6552     
6553     enable : function()
6554     {
6555         this.setDisabled(false);
6556     },
6557     
6558     setDisabled : function(state)
6559     {
6560         if(this.disabled == state){
6561             return;
6562         }
6563         
6564         this.disabled = state;
6565         
6566         if (state) {
6567             this.el.addClass('disabled');
6568             return;
6569         }
6570         
6571         this.el.removeClass('disabled');
6572         
6573         return;
6574     },
6575     
6576     setActive : function(state)
6577     {
6578         if(this.active == state){
6579             return;
6580         }
6581         
6582         this.active = state;
6583         
6584         if (state) {
6585             this.el.addClass('active');
6586             return;
6587         }
6588         
6589         this.el.removeClass('active');
6590         
6591         return;
6592     },
6593     
6594     isActive: function () 
6595     {
6596         return this.active;
6597     },
6598     
6599     setBadge : function(str)
6600     {
6601         if(!this.badgeEl){
6602             return;
6603         }
6604         
6605         this.badgeEl.dom.innerHTML = str;
6606     }
6607     
6608    
6609      
6610  
6611 });
6612  
6613
6614  /*
6615  * - LGPL
6616  *
6617  *  Breadcrumb Nav
6618  * 
6619  */
6620 Roo.namespace('Roo.bootstrap.breadcrumb');
6621
6622
6623 /**
6624  * @class Roo.bootstrap.breadcrumb.Nav
6625  * @extends Roo.bootstrap.Component
6626  * Bootstrap Breadcrumb Nav Class
6627  *  
6628  * @children Roo.bootstrap.breadcrumb.Item
6629  * 
6630  * @constructor
6631  * Create a new breadcrumb.Nav
6632  * @param {Object} config The config object
6633  */
6634
6635
6636 Roo.bootstrap.breadcrumb.Nav = function(config){
6637     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6638     
6639     
6640 };
6641
6642 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6643     
6644     getAutoCreate : function()
6645     {
6646
6647         var cfg = {
6648             tag: 'nav',
6649             cn : [
6650                 {
6651                     tag : 'ol',
6652                     cls : 'breadcrumb'
6653                 }
6654             ]
6655             
6656         };
6657           
6658         return cfg;
6659     },
6660     
6661     initEvents: function()
6662     {
6663         this.olEl = this.el.select('ol',true).first();    
6664     },
6665     getChildContainer : function()
6666     {
6667         return this.olEl;  
6668     }
6669     
6670 });
6671
6672  /*
6673  * - LGPL
6674  *
6675  *  Breadcrumb Item
6676  * 
6677  */
6678
6679
6680 /**
6681  * @class Roo.bootstrap.breadcrumb.Nav
6682  * @extends Roo.bootstrap.Component
6683  * Bootstrap Breadcrumb Nav Class
6684  *  
6685  * @children Roo.bootstrap.breadcrumb.Component
6686  * @cfg {String} html the content of the link.
6687  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6688  * @cfg {Boolean} active is it active
6689
6690  * 
6691  * @constructor
6692  * Create a new breadcrumb.Nav
6693  * @param {Object} config The config object
6694  */
6695
6696 Roo.bootstrap.breadcrumb.Item = function(config){
6697     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6698     this.addEvents({
6699         // img events
6700         /**
6701          * @event click
6702          * The img click event for the img.
6703          * @param {Roo.EventObject} e
6704          */
6705         "click" : true
6706     });
6707     
6708 };
6709
6710 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6711     
6712     href: false,
6713     html : '',
6714     
6715     getAutoCreate : function()
6716     {
6717
6718         var cfg = {
6719             tag: 'li',
6720             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6721         };
6722         if (this.href !== false) {
6723             cfg.cn = [{
6724                 tag : 'a',
6725                 href : this.href,
6726                 html : this.html
6727             }];
6728         } else {
6729             cfg.html = this.html;
6730         }
6731         
6732         return cfg;
6733     },
6734     
6735     initEvents: function()
6736     {
6737         if (this.href) {
6738             this.el.select('a', true).first().on('click',this.onClick, this)
6739         }
6740         
6741     },
6742     onClick : function(e)
6743     {
6744         e.preventDefault();
6745         this.fireEvent('click',this,  e);
6746     }
6747     
6748 });
6749
6750  /*
6751  * - LGPL
6752  *
6753  * row
6754  * 
6755  */
6756
6757 /**
6758  * @class Roo.bootstrap.Row
6759  * @extends Roo.bootstrap.Component
6760  * Bootstrap Row class (contains columns...)
6761  * 
6762  * @constructor
6763  * Create a new Row
6764  * @param {Object} config The config object
6765  */
6766
6767 Roo.bootstrap.Row = function(config){
6768     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6769 };
6770
6771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6772     
6773     getAutoCreate : function(){
6774        return {
6775             cls: 'row clearfix'
6776        };
6777     }
6778     
6779     
6780 });
6781
6782  
6783
6784  /*
6785  * - LGPL
6786  *
6787  * pagination
6788  * 
6789  */
6790
6791 /**
6792  * @class Roo.bootstrap.Pagination
6793  * @extends Roo.bootstrap.Component
6794  * Bootstrap Pagination class
6795  * @cfg {String} size xs | sm | md | lg
6796  * @cfg {Boolean} inverse false | true
6797  * 
6798  * @constructor
6799  * Create a new Pagination
6800  * @param {Object} config The config object
6801  */
6802
6803 Roo.bootstrap.Pagination = function(config){
6804     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6805 };
6806
6807 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6808     
6809     cls: false,
6810     size: false,
6811     inverse: false,
6812     
6813     getAutoCreate : function(){
6814         var cfg = {
6815             tag: 'ul',
6816                 cls: 'pagination'
6817         };
6818         if (this.inverse) {
6819             cfg.cls += ' inverse';
6820         }
6821         if (this.html) {
6822             cfg.html=this.html;
6823         }
6824         if (this.cls) {
6825             cfg.cls += " " + this.cls;
6826         }
6827         return cfg;
6828     }
6829    
6830 });
6831
6832  
6833
6834  /*
6835  * - LGPL
6836  *
6837  * Pagination item
6838  * 
6839  */
6840
6841
6842 /**
6843  * @class Roo.bootstrap.PaginationItem
6844  * @extends Roo.bootstrap.Component
6845  * Bootstrap PaginationItem class
6846  * @cfg {String} html text
6847  * @cfg {String} href the link
6848  * @cfg {Boolean} preventDefault (true | false) default true
6849  * @cfg {Boolean} active (true | false) default false
6850  * @cfg {Boolean} disabled default false
6851  * 
6852  * 
6853  * @constructor
6854  * Create a new PaginationItem
6855  * @param {Object} config The config object
6856  */
6857
6858
6859 Roo.bootstrap.PaginationItem = function(config){
6860     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6861     this.addEvents({
6862         // raw events
6863         /**
6864          * @event click
6865          * The raw click event for the entire grid.
6866          * @param {Roo.EventObject} e
6867          */
6868         "click" : true
6869     });
6870 };
6871
6872 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6873     
6874     href : false,
6875     html : false,
6876     preventDefault: true,
6877     active : false,
6878     cls : false,
6879     disabled: false,
6880     
6881     getAutoCreate : function(){
6882         var cfg= {
6883             tag: 'li',
6884             cn: [
6885                 {
6886                     tag : 'a',
6887                     href : this.href ? this.href : '#',
6888                     html : this.html ? this.html : ''
6889                 }
6890             ]
6891         };
6892         
6893         if(this.cls){
6894             cfg.cls = this.cls;
6895         }
6896         
6897         if(this.disabled){
6898             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6899         }
6900         
6901         if(this.active){
6902             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6903         }
6904         
6905         return cfg;
6906     },
6907     
6908     initEvents: function() {
6909         
6910         this.el.on('click', this.onClick, this);
6911         
6912     },
6913     onClick : function(e)
6914     {
6915         Roo.log('PaginationItem on click ');
6916         if(this.preventDefault){
6917             e.preventDefault();
6918         }
6919         
6920         if(this.disabled){
6921             return;
6922         }
6923         
6924         this.fireEvent('click', this, e);
6925     }
6926    
6927 });
6928
6929  
6930
6931  /*
6932  * - LGPL
6933  *
6934  * slider
6935  * 
6936  */
6937
6938
6939 /**
6940  * @class Roo.bootstrap.Slider
6941  * @extends Roo.bootstrap.Component
6942  * Bootstrap Slider class
6943  *    
6944  * @constructor
6945  * Create a new Slider
6946  * @param {Object} config The config object
6947  */
6948
6949 Roo.bootstrap.Slider = function(config){
6950     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6951 };
6952
6953 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6954     
6955     getAutoCreate : function(){
6956         
6957         var cfg = {
6958             tag: 'div',
6959             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6960             cn: [
6961                 {
6962                     tag: 'a',
6963                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6964                 }
6965             ]
6966         };
6967         
6968         return cfg;
6969     }
6970    
6971 });
6972
6973  /*
6974  * Based on:
6975  * Ext JS Library 1.1.1
6976  * Copyright(c) 2006-2007, Ext JS, LLC.
6977  *
6978  * Originally Released Under LGPL - original licence link has changed is not relivant.
6979  *
6980  * Fork - LGPL
6981  * <script type="text/javascript">
6982  */
6983  
6984
6985 /**
6986  * @class Roo.grid.ColumnModel
6987  * @extends Roo.util.Observable
6988  * This is the default implementation of a ColumnModel used by the Grid. It defines
6989  * the columns in the grid.
6990  * <br>Usage:<br>
6991  <pre><code>
6992  var colModel = new Roo.grid.ColumnModel([
6993         {header: "Ticker", width: 60, sortable: true, locked: true},
6994         {header: "Company Name", width: 150, sortable: true},
6995         {header: "Market Cap.", width: 100, sortable: true},
6996         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6997         {header: "Employees", width: 100, sortable: true, resizable: false}
6998  ]);
6999  </code></pre>
7000  * <p>
7001  
7002  * The config options listed for this class are options which may appear in each
7003  * individual column definition.
7004  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7005  * @constructor
7006  * @param {Object} config An Array of column config objects. See this class's
7007  * config objects for details.
7008 */
7009 Roo.grid.ColumnModel = function(config){
7010         /**
7011      * The config passed into the constructor
7012      */
7013     this.config = config;
7014     this.lookup = {};
7015
7016     // if no id, create one
7017     // if the column does not have a dataIndex mapping,
7018     // map it to the order it is in the config
7019     for(var i = 0, len = config.length; i < len; i++){
7020         var c = config[i];
7021         if(typeof c.dataIndex == "undefined"){
7022             c.dataIndex = i;
7023         }
7024         if(typeof c.renderer == "string"){
7025             c.renderer = Roo.util.Format[c.renderer];
7026         }
7027         if(typeof c.id == "undefined"){
7028             c.id = Roo.id();
7029         }
7030         if(c.editor && c.editor.xtype){
7031             c.editor  = Roo.factory(c.editor, Roo.grid);
7032         }
7033         if(c.editor && c.editor.isFormField){
7034             c.editor = new Roo.grid.GridEditor(c.editor);
7035         }
7036         this.lookup[c.id] = c;
7037     }
7038
7039     /**
7040      * The width of columns which have no width specified (defaults to 100)
7041      * @type Number
7042      */
7043     this.defaultWidth = 100;
7044
7045     /**
7046      * Default sortable of columns which have no sortable specified (defaults to false)
7047      * @type Boolean
7048      */
7049     this.defaultSortable = false;
7050
7051     this.addEvents({
7052         /**
7053              * @event widthchange
7054              * Fires when the width of a column changes.
7055              * @param {ColumnModel} this
7056              * @param {Number} columnIndex The column index
7057              * @param {Number} newWidth The new width
7058              */
7059             "widthchange": true,
7060         /**
7061              * @event headerchange
7062              * Fires when the text of a header changes.
7063              * @param {ColumnModel} this
7064              * @param {Number} columnIndex The column index
7065              * @param {Number} newText The new header text
7066              */
7067             "headerchange": true,
7068         /**
7069              * @event hiddenchange
7070              * Fires when a column is hidden or "unhidden".
7071              * @param {ColumnModel} this
7072              * @param {Number} columnIndex The column index
7073              * @param {Boolean} hidden true if hidden, false otherwise
7074              */
7075             "hiddenchange": true,
7076             /**
7077          * @event columnmoved
7078          * Fires when a column is moved.
7079          * @param {ColumnModel} this
7080          * @param {Number} oldIndex
7081          * @param {Number} newIndex
7082          */
7083         "columnmoved" : true,
7084         /**
7085          * @event columlockchange
7086          * Fires when a column's locked state is changed
7087          * @param {ColumnModel} this
7088          * @param {Number} colIndex
7089          * @param {Boolean} locked true if locked
7090          */
7091         "columnlockchange" : true
7092     });
7093     Roo.grid.ColumnModel.superclass.constructor.call(this);
7094 };
7095 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7096     /**
7097      * @cfg {String} header The header text to display in the Grid view.
7098      */
7099     /**
7100      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7101      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7102      * specified, the column's index is used as an index into the Record's data Array.
7103      */
7104     /**
7105      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7106      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7107      */
7108     /**
7109      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7110      * Defaults to the value of the {@link #defaultSortable} property.
7111      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7112      */
7113     /**
7114      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7115      */
7116     /**
7117      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7121      */
7122     /**
7123      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7124      */
7125     /**
7126      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7127      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7128      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7129      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7130      */
7131        /**
7132      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7133      */
7134     /**
7135      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7136      */
7137     /**
7138      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} cursor (Optional)
7142      */
7143     /**
7144      * @cfg {String} tooltip (Optional)
7145      */
7146     /**
7147      * @cfg {Number} xs (Optional)
7148      */
7149     /**
7150      * @cfg {Number} sm (Optional)
7151      */
7152     /**
7153      * @cfg {Number} md (Optional)
7154      */
7155     /**
7156      * @cfg {Number} lg (Optional)
7157      */
7158     /**
7159      * Returns the id of the column at the specified index.
7160      * @param {Number} index The column index
7161      * @return {String} the id
7162      */
7163     getColumnId : function(index){
7164         return this.config[index].id;
7165     },
7166
7167     /**
7168      * Returns the column for a specified id.
7169      * @param {String} id The column id
7170      * @return {Object} the column
7171      */
7172     getColumnById : function(id){
7173         return this.lookup[id];
7174     },
7175
7176     
7177     /**
7178      * Returns the column for a specified dataIndex.
7179      * @param {String} dataIndex The column dataIndex
7180      * @return {Object|Boolean} the column or false if not found
7181      */
7182     getColumnByDataIndex: function(dataIndex){
7183         var index = this.findColumnIndex(dataIndex);
7184         return index > -1 ? this.config[index] : false;
7185     },
7186     
7187     /**
7188      * Returns the index for a specified column id.
7189      * @param {String} id The column id
7190      * @return {Number} the index, or -1 if not found
7191      */
7192     getIndexById : function(id){
7193         for(var i = 0, len = this.config.length; i < len; i++){
7194             if(this.config[i].id == id){
7195                 return i;
7196             }
7197         }
7198         return -1;
7199     },
7200     
7201     /**
7202      * Returns the index for a specified column dataIndex.
7203      * @param {String} dataIndex The column dataIndex
7204      * @return {Number} the index, or -1 if not found
7205      */
7206     
7207     findColumnIndex : function(dataIndex){
7208         for(var i = 0, len = this.config.length; i < len; i++){
7209             if(this.config[i].dataIndex == dataIndex){
7210                 return i;
7211             }
7212         }
7213         return -1;
7214     },
7215     
7216     
7217     moveColumn : function(oldIndex, newIndex){
7218         var c = this.config[oldIndex];
7219         this.config.splice(oldIndex, 1);
7220         this.config.splice(newIndex, 0, c);
7221         this.dataMap = null;
7222         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7223     },
7224
7225     isLocked : function(colIndex){
7226         return this.config[colIndex].locked === true;
7227     },
7228
7229     setLocked : function(colIndex, value, suppressEvent){
7230         if(this.isLocked(colIndex) == value){
7231             return;
7232         }
7233         this.config[colIndex].locked = value;
7234         if(!suppressEvent){
7235             this.fireEvent("columnlockchange", this, colIndex, value);
7236         }
7237     },
7238
7239     getTotalLockedWidth : function(){
7240         var totalWidth = 0;
7241         for(var i = 0; i < this.config.length; i++){
7242             if(this.isLocked(i) && !this.isHidden(i)){
7243                 this.totalWidth += this.getColumnWidth(i);
7244             }
7245         }
7246         return totalWidth;
7247     },
7248
7249     getLockedCount : function(){
7250         for(var i = 0, len = this.config.length; i < len; i++){
7251             if(!this.isLocked(i)){
7252                 return i;
7253             }
7254         }
7255         
7256         return this.config.length;
7257     },
7258
7259     /**
7260      * Returns the number of columns.
7261      * @return {Number}
7262      */
7263     getColumnCount : function(visibleOnly){
7264         if(visibleOnly === true){
7265             var c = 0;
7266             for(var i = 0, len = this.config.length; i < len; i++){
7267                 if(!this.isHidden(i)){
7268                     c++;
7269                 }
7270             }
7271             return c;
7272         }
7273         return this.config.length;
7274     },
7275
7276     /**
7277      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7278      * @param {Function} fn
7279      * @param {Object} scope (optional)
7280      * @return {Array} result
7281      */
7282     getColumnsBy : function(fn, scope){
7283         var r = [];
7284         for(var i = 0, len = this.config.length; i < len; i++){
7285             var c = this.config[i];
7286             if(fn.call(scope||this, c, i) === true){
7287                 r[r.length] = c;
7288             }
7289         }
7290         return r;
7291     },
7292
7293     /**
7294      * Returns true if the specified column is sortable.
7295      * @param {Number} col The column index
7296      * @return {Boolean}
7297      */
7298     isSortable : function(col){
7299         if(typeof this.config[col].sortable == "undefined"){
7300             return this.defaultSortable;
7301         }
7302         return this.config[col].sortable;
7303     },
7304
7305     /**
7306      * Returns the rendering (formatting) function defined for the column.
7307      * @param {Number} col The column index.
7308      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7309      */
7310     getRenderer : function(col){
7311         if(!this.config[col].renderer){
7312             return Roo.grid.ColumnModel.defaultRenderer;
7313         }
7314         return this.config[col].renderer;
7315     },
7316
7317     /**
7318      * Sets the rendering (formatting) function for a column.
7319      * @param {Number} col The column index
7320      * @param {Function} fn The function to use to process the cell's raw data
7321      * to return HTML markup for the grid view. The render function is called with
7322      * the following parameters:<ul>
7323      * <li>Data value.</li>
7324      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7325      * <li>css A CSS style string to apply to the table cell.</li>
7326      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7327      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7328      * <li>Row index</li>
7329      * <li>Column index</li>
7330      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7331      */
7332     setRenderer : function(col, fn){
7333         this.config[col].renderer = fn;
7334     },
7335
7336     /**
7337      * Returns the width for the specified column.
7338      * @param {Number} col The column index
7339      * @return {Number}
7340      */
7341     getColumnWidth : function(col){
7342         return this.config[col].width * 1 || this.defaultWidth;
7343     },
7344
7345     /**
7346      * Sets the width for a column.
7347      * @param {Number} col The column index
7348      * @param {Number} width The new width
7349      */
7350     setColumnWidth : function(col, width, suppressEvent){
7351         this.config[col].width = width;
7352         this.totalWidth = null;
7353         if(!suppressEvent){
7354              this.fireEvent("widthchange", this, col, width);
7355         }
7356     },
7357
7358     /**
7359      * Returns the total width of all columns.
7360      * @param {Boolean} includeHidden True to include hidden column widths
7361      * @return {Number}
7362      */
7363     getTotalWidth : function(includeHidden){
7364         if(!this.totalWidth){
7365             this.totalWidth = 0;
7366             for(var i = 0, len = this.config.length; i < len; i++){
7367                 if(includeHidden || !this.isHidden(i)){
7368                     this.totalWidth += this.getColumnWidth(i);
7369                 }
7370             }
7371         }
7372         return this.totalWidth;
7373     },
7374
7375     /**
7376      * Returns the header for the specified column.
7377      * @param {Number} col The column index
7378      * @return {String}
7379      */
7380     getColumnHeader : function(col){
7381         return this.config[col].header;
7382     },
7383
7384     /**
7385      * Sets the header for a column.
7386      * @param {Number} col The column index
7387      * @param {String} header The new header
7388      */
7389     setColumnHeader : function(col, header){
7390         this.config[col].header = header;
7391         this.fireEvent("headerchange", this, col, header);
7392     },
7393
7394     /**
7395      * Returns the tooltip for the specified column.
7396      * @param {Number} col The column index
7397      * @return {String}
7398      */
7399     getColumnTooltip : function(col){
7400             return this.config[col].tooltip;
7401     },
7402     /**
7403      * Sets the tooltip for a column.
7404      * @param {Number} col The column index
7405      * @param {String} tooltip The new tooltip
7406      */
7407     setColumnTooltip : function(col, tooltip){
7408             this.config[col].tooltip = tooltip;
7409     },
7410
7411     /**
7412      * Returns the dataIndex for the specified column.
7413      * @param {Number} col The column index
7414      * @return {Number}
7415      */
7416     getDataIndex : function(col){
7417         return this.config[col].dataIndex;
7418     },
7419
7420     /**
7421      * Sets the dataIndex for a column.
7422      * @param {Number} col The column index
7423      * @param {Number} dataIndex The new dataIndex
7424      */
7425     setDataIndex : function(col, dataIndex){
7426         this.config[col].dataIndex = dataIndex;
7427     },
7428
7429     
7430     
7431     /**
7432      * Returns true if the cell is editable.
7433      * @param {Number} colIndex The column index
7434      * @param {Number} rowIndex The row index - this is nto actually used..?
7435      * @return {Boolean}
7436      */
7437     isCellEditable : function(colIndex, rowIndex){
7438         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7439     },
7440
7441     /**
7442      * Returns the editor defined for the cell/column.
7443      * return false or null to disable editing.
7444      * @param {Number} colIndex The column index
7445      * @param {Number} rowIndex The row index
7446      * @return {Object}
7447      */
7448     getCellEditor : function(colIndex, rowIndex){
7449         return this.config[colIndex].editor;
7450     },
7451
7452     /**
7453      * Sets if a column is editable.
7454      * @param {Number} col The column index
7455      * @param {Boolean} editable True if the column is editable
7456      */
7457     setEditable : function(col, editable){
7458         this.config[col].editable = editable;
7459     },
7460
7461
7462     /**
7463      * Returns true if the column is hidden.
7464      * @param {Number} colIndex The column index
7465      * @return {Boolean}
7466      */
7467     isHidden : function(colIndex){
7468         return this.config[colIndex].hidden;
7469     },
7470
7471
7472     /**
7473      * Returns true if the column width cannot be changed
7474      */
7475     isFixed : function(colIndex){
7476         return this.config[colIndex].fixed;
7477     },
7478
7479     /**
7480      * Returns true if the column can be resized
7481      * @return {Boolean}
7482      */
7483     isResizable : function(colIndex){
7484         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7485     },
7486     /**
7487      * Sets if a column is hidden.
7488      * @param {Number} colIndex The column index
7489      * @param {Boolean} hidden True if the column is hidden
7490      */
7491     setHidden : function(colIndex, hidden){
7492         this.config[colIndex].hidden = hidden;
7493         this.totalWidth = null;
7494         this.fireEvent("hiddenchange", this, colIndex, hidden);
7495     },
7496
7497     /**
7498      * Sets the editor for a column.
7499      * @param {Number} col The column index
7500      * @param {Object} editor The editor object
7501      */
7502     setEditor : function(col, editor){
7503         this.config[col].editor = editor;
7504     }
7505 });
7506
7507 Roo.grid.ColumnModel.defaultRenderer = function(value)
7508 {
7509     if(typeof value == "object") {
7510         return value;
7511     }
7512         if(typeof value == "string" && value.length < 1){
7513             return "&#160;";
7514         }
7515     
7516         return String.format("{0}", value);
7517 };
7518
7519 // Alias for backwards compatibility
7520 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7521 /*
7522  * Based on:
7523  * Ext JS Library 1.1.1
7524  * Copyright(c) 2006-2007, Ext JS, LLC.
7525  *
7526  * Originally Released Under LGPL - original licence link has changed is not relivant.
7527  *
7528  * Fork - LGPL
7529  * <script type="text/javascript">
7530  */
7531  
7532 /**
7533  * @class Roo.LoadMask
7534  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7535  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7536  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7537  * element's UpdateManager load indicator and will be destroyed after the initial load.
7538  * @constructor
7539  * Create a new LoadMask
7540  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7541  * @param {Object} config The config object
7542  */
7543 Roo.LoadMask = function(el, config){
7544     this.el = Roo.get(el);
7545     Roo.apply(this, config);
7546     if(this.store){
7547         this.store.on('beforeload', this.onBeforeLoad, this);
7548         this.store.on('load', this.onLoad, this);
7549         this.store.on('loadexception', this.onLoadException, this);
7550         this.removeMask = false;
7551     }else{
7552         var um = this.el.getUpdateManager();
7553         um.showLoadIndicator = false; // disable the default indicator
7554         um.on('beforeupdate', this.onBeforeLoad, this);
7555         um.on('update', this.onLoad, this);
7556         um.on('failure', this.onLoad, this);
7557         this.removeMask = true;
7558     }
7559 };
7560
7561 Roo.LoadMask.prototype = {
7562     /**
7563      * @cfg {Boolean} removeMask
7564      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7565      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7566      */
7567     /**
7568      * @cfg {String} msg
7569      * The text to display in a centered loading message box (defaults to 'Loading...')
7570      */
7571     msg : 'Loading...',
7572     /**
7573      * @cfg {String} msgCls
7574      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7575      */
7576     msgCls : 'x-mask-loading',
7577
7578     /**
7579      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7580      * @type Boolean
7581      */
7582     disabled: false,
7583
7584     /**
7585      * Disables the mask to prevent it from being displayed
7586      */
7587     disable : function(){
7588        this.disabled = true;
7589     },
7590
7591     /**
7592      * Enables the mask so that it can be displayed
7593      */
7594     enable : function(){
7595         this.disabled = false;
7596     },
7597     
7598     onLoadException : function()
7599     {
7600         Roo.log(arguments);
7601         
7602         if (typeof(arguments[3]) != 'undefined') {
7603             Roo.MessageBox.alert("Error loading",arguments[3]);
7604         } 
7605         /*
7606         try {
7607             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7608                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7609             }   
7610         } catch(e) {
7611             
7612         }
7613         */
7614     
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617     // private
7618     onLoad : function()
7619     {
7620         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7621     },
7622
7623     // private
7624     onBeforeLoad : function(){
7625         if(!this.disabled){
7626             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7627         }
7628     },
7629
7630     // private
7631     destroy : function(){
7632         if(this.store){
7633             this.store.un('beforeload', this.onBeforeLoad, this);
7634             this.store.un('load', this.onLoad, this);
7635             this.store.un('loadexception', this.onLoadException, this);
7636         }else{
7637             var um = this.el.getUpdateManager();
7638             um.un('beforeupdate', this.onBeforeLoad, this);
7639             um.un('update', this.onLoad, this);
7640             um.un('failure', this.onLoad, this);
7641         }
7642     }
7643 };/*
7644  * - LGPL
7645  *
7646  * table
7647  * 
7648  */
7649
7650 /**
7651  * @class Roo.bootstrap.Table
7652  * @extends Roo.bootstrap.Component
7653  * Bootstrap Table class
7654  * @cfg {String} cls table class
7655  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7656  * @cfg {String} bgcolor Specifies the background color for a table
7657  * @cfg {Number} border Specifies whether the table cells should have borders or not
7658  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7659  * @cfg {Number} cellspacing Specifies the space between cells
7660  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7661  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7662  * @cfg {String} sortable Specifies that the table should be sortable
7663  * @cfg {String} summary Specifies a summary of the content of a table
7664  * @cfg {Number} width Specifies the width of a table
7665  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7666  * 
7667  * @cfg {boolean} striped Should the rows be alternative striped
7668  * @cfg {boolean} bordered Add borders to the table
7669  * @cfg {boolean} hover Add hover highlighting
7670  * @cfg {boolean} condensed Format condensed
7671  * @cfg {boolean} responsive Format condensed
7672  * @cfg {Boolean} loadMask (true|false) default false
7673  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7674  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7675  * @cfg {Boolean} rowSelection (true|false) default false
7676  * @cfg {Boolean} cellSelection (true|false) default false
7677  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7678  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7679  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7680  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7681  
7682  * 
7683  * @constructor
7684  * Create a new Table
7685  * @param {Object} config The config object
7686  */
7687
7688 Roo.bootstrap.Table = function(config){
7689     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7690     
7691   
7692     
7693     // BC...
7694     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7695     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7696     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7697     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7698     
7699     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7700     if (this.sm) {
7701         this.sm.grid = this;
7702         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7703         this.sm = this.selModel;
7704         this.sm.xmodule = this.xmodule || false;
7705     }
7706     
7707     if (this.cm && typeof(this.cm.config) == 'undefined') {
7708         this.colModel = new Roo.grid.ColumnModel(this.cm);
7709         this.cm = this.colModel;
7710         this.cm.xmodule = this.xmodule || false;
7711     }
7712     if (this.store) {
7713         this.store= Roo.factory(this.store, Roo.data);
7714         this.ds = this.store;
7715         this.ds.xmodule = this.xmodule || false;
7716          
7717     }
7718     if (this.footer && this.store) {
7719         this.footer.dataSource = this.ds;
7720         this.footer = Roo.factory(this.footer);
7721     }
7722     
7723     /** @private */
7724     this.addEvents({
7725         /**
7726          * @event cellclick
7727          * Fires when a cell is clicked
7728          * @param {Roo.bootstrap.Table} this
7729          * @param {Roo.Element} el
7730          * @param {Number} rowIndex
7731          * @param {Number} columnIndex
7732          * @param {Roo.EventObject} e
7733          */
7734         "cellclick" : true,
7735         /**
7736          * @event celldblclick
7737          * Fires when a cell is double clicked
7738          * @param {Roo.bootstrap.Table} this
7739          * @param {Roo.Element} el
7740          * @param {Number} rowIndex
7741          * @param {Number} columnIndex
7742          * @param {Roo.EventObject} e
7743          */
7744         "celldblclick" : true,
7745         /**
7746          * @event rowclick
7747          * Fires when a row is clicked
7748          * @param {Roo.bootstrap.Table} this
7749          * @param {Roo.Element} el
7750          * @param {Number} rowIndex
7751          * @param {Roo.EventObject} e
7752          */
7753         "rowclick" : true,
7754         /**
7755          * @event rowdblclick
7756          * Fires when a row is double clicked
7757          * @param {Roo.bootstrap.Table} this
7758          * @param {Roo.Element} el
7759          * @param {Number} rowIndex
7760          * @param {Roo.EventObject} e
7761          */
7762         "rowdblclick" : true,
7763         /**
7764          * @event mouseover
7765          * Fires when a mouseover occur
7766          * @param {Roo.bootstrap.Table} this
7767          * @param {Roo.Element} el
7768          * @param {Number} rowIndex
7769          * @param {Number} columnIndex
7770          * @param {Roo.EventObject} e
7771          */
7772         "mouseover" : true,
7773         /**
7774          * @event mouseout
7775          * Fires when a mouseout occur
7776          * @param {Roo.bootstrap.Table} this
7777          * @param {Roo.Element} el
7778          * @param {Number} rowIndex
7779          * @param {Number} columnIndex
7780          * @param {Roo.EventObject} e
7781          */
7782         "mouseout" : true,
7783         /**
7784          * @event rowclass
7785          * Fires when a row is rendered, so you can change add a style to it.
7786          * @param {Roo.bootstrap.Table} this
7787          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7788          */
7789         'rowclass' : true,
7790           /**
7791          * @event rowsrendered
7792          * Fires when all the  rows have been rendered
7793          * @param {Roo.bootstrap.Table} this
7794          */
7795         'rowsrendered' : true,
7796         /**
7797          * @event contextmenu
7798          * The raw contextmenu event for the entire grid.
7799          * @param {Roo.EventObject} e
7800          */
7801         "contextmenu" : true,
7802         /**
7803          * @event rowcontextmenu
7804          * Fires when a row is right clicked
7805          * @param {Roo.bootstrap.Table} this
7806          * @param {Number} rowIndex
7807          * @param {Roo.EventObject} e
7808          */
7809         "rowcontextmenu" : true,
7810         /**
7811          * @event cellcontextmenu
7812          * Fires when a cell is right clicked
7813          * @param {Roo.bootstrap.Table} this
7814          * @param {Number} rowIndex
7815          * @param {Number} cellIndex
7816          * @param {Roo.EventObject} e
7817          */
7818          "cellcontextmenu" : true,
7819          /**
7820          * @event headercontextmenu
7821          * Fires when a header is right clicked
7822          * @param {Roo.bootstrap.Table} this
7823          * @param {Number} columnIndex
7824          * @param {Roo.EventObject} e
7825          */
7826         "headercontextmenu" : true
7827     });
7828 };
7829
7830 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7831     
7832     cls: false,
7833     align: false,
7834     bgcolor: false,
7835     border: false,
7836     cellpadding: false,
7837     cellspacing: false,
7838     frame: false,
7839     rules: false,
7840     sortable: false,
7841     summary: false,
7842     width: false,
7843     striped : false,
7844     scrollBody : false,
7845     bordered: false,
7846     hover:  false,
7847     condensed : false,
7848     responsive : false,
7849     sm : false,
7850     cm : false,
7851     store : false,
7852     loadMask : false,
7853     footerShow : true,
7854     headerShow : true,
7855   
7856     rowSelection : false,
7857     cellSelection : false,
7858     layout : false,
7859     
7860     // Roo.Element - the tbody
7861     mainBody: false,
7862     // Roo.Element - thead element
7863     mainHead: false,
7864     
7865     container: false, // used by gridpanel...
7866     
7867     lazyLoad : false,
7868     
7869     CSS : Roo.util.CSS,
7870     
7871     auto_hide_footer : false,
7872     
7873     getAutoCreate : function()
7874     {
7875         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7876         
7877         cfg = {
7878             tag: 'table',
7879             cls : 'table',
7880             cn : []
7881         };
7882         if (this.scrollBody) {
7883             cfg.cls += ' table-body-fixed';
7884         }    
7885         if (this.striped) {
7886             cfg.cls += ' table-striped';
7887         }
7888         
7889         if (this.hover) {
7890             cfg.cls += ' table-hover';
7891         }
7892         if (this.bordered) {
7893             cfg.cls += ' table-bordered';
7894         }
7895         if (this.condensed) {
7896             cfg.cls += ' table-condensed';
7897         }
7898         if (this.responsive) {
7899             cfg.cls += ' table-responsive';
7900         }
7901         
7902         if (this.cls) {
7903             cfg.cls+=  ' ' +this.cls;
7904         }
7905         
7906         // this lot should be simplifed...
7907         var _t = this;
7908         var cp = [
7909             'align',
7910             'bgcolor',
7911             'border',
7912             'cellpadding',
7913             'cellspacing',
7914             'frame',
7915             'rules',
7916             'sortable',
7917             'summary',
7918             'width'
7919         ].forEach(function(k) {
7920             if (_t[k]) {
7921                 cfg[k] = _t[k];
7922             }
7923         });
7924         
7925         
7926         if (this.layout) {
7927             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7928         }
7929         
7930         if(this.store || this.cm){
7931             if(this.headerShow){
7932                 cfg.cn.push(this.renderHeader());
7933             }
7934             
7935             cfg.cn.push(this.renderBody());
7936             
7937             if(this.footerShow){
7938                 cfg.cn.push(this.renderFooter());
7939             }
7940             // where does this come from?
7941             //cfg.cls+=  ' TableGrid';
7942         }
7943         
7944         return { cn : [ cfg ] };
7945     },
7946     
7947     initEvents : function()
7948     {   
7949         if(!this.store || !this.cm){
7950             return;
7951         }
7952         if (this.selModel) {
7953             this.selModel.initEvents();
7954         }
7955         
7956         
7957         //Roo.log('initEvents with ds!!!!');
7958         
7959         this.mainBody = this.el.select('tbody', true).first();
7960         this.mainHead = this.el.select('thead', true).first();
7961         this.mainFoot = this.el.select('tfoot', true).first();
7962         
7963         
7964         
7965         var _this = this;
7966         
7967         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7968             e.on('click', _this.sort, _this);
7969         });
7970         
7971         this.mainBody.on("click", this.onClick, this);
7972         this.mainBody.on("dblclick", this.onDblClick, this);
7973         
7974         // why is this done????? = it breaks dialogs??
7975         //this.parent().el.setStyle('position', 'relative');
7976         
7977         
7978         if (this.footer) {
7979             this.footer.parentId = this.id;
7980             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7981             
7982             if(this.lazyLoad){
7983                 this.el.select('tfoot tr td').first().addClass('hide');
7984             }
7985         } 
7986         
7987         if(this.loadMask) {
7988             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7989         }
7990         
7991         this.store.on('load', this.onLoad, this);
7992         this.store.on('beforeload', this.onBeforeLoad, this);
7993         this.store.on('update', this.onUpdate, this);
7994         this.store.on('add', this.onAdd, this);
7995         this.store.on("clear", this.clear, this);
7996         
7997         this.el.on("contextmenu", this.onContextMenu, this);
7998         
7999         this.mainBody.on('scroll', this.onBodyScroll, this);
8000         
8001         this.cm.on("headerchange", this.onHeaderChange, this);
8002         
8003         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8004         
8005     },
8006     
8007     onContextMenu : function(e, t)
8008     {
8009         this.processEvent("contextmenu", e);
8010     },
8011     
8012     processEvent : function(name, e)
8013     {
8014         if (name != 'touchstart' ) {
8015             this.fireEvent(name, e);    
8016         }
8017         
8018         var t = e.getTarget();
8019         
8020         var cell = Roo.get(t);
8021         
8022         if(!cell){
8023             return;
8024         }
8025         
8026         if(cell.findParent('tfoot', false, true)){
8027             return;
8028         }
8029         
8030         if(cell.findParent('thead', false, true)){
8031             
8032             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8033                 cell = Roo.get(t).findParent('th', false, true);
8034                 if (!cell) {
8035                     Roo.log("failed to find th in thead?");
8036                     Roo.log(e.getTarget());
8037                     return;
8038                 }
8039             }
8040             
8041             var cellIndex = cell.dom.cellIndex;
8042             
8043             var ename = name == 'touchstart' ? 'click' : name;
8044             this.fireEvent("header" + ename, this, cellIndex, e);
8045             
8046             return;
8047         }
8048         
8049         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8050             cell = Roo.get(t).findParent('td', false, true);
8051             if (!cell) {
8052                 Roo.log("failed to find th in tbody?");
8053                 Roo.log(e.getTarget());
8054                 return;
8055             }
8056         }
8057         
8058         var row = cell.findParent('tr', false, true);
8059         var cellIndex = cell.dom.cellIndex;
8060         var rowIndex = row.dom.rowIndex - 1;
8061         
8062         if(row !== false){
8063             
8064             this.fireEvent("row" + name, this, rowIndex, e);
8065             
8066             if(cell !== false){
8067             
8068                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8069             }
8070         }
8071         
8072     },
8073     
8074     onMouseover : function(e, el)
8075     {
8076         var cell = Roo.get(el);
8077         
8078         if(!cell){
8079             return;
8080         }
8081         
8082         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8083             cell = cell.findParent('td', false, true);
8084         }
8085         
8086         var row = cell.findParent('tr', false, true);
8087         var cellIndex = cell.dom.cellIndex;
8088         var rowIndex = row.dom.rowIndex - 1; // start from 0
8089         
8090         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8091         
8092     },
8093     
8094     onMouseout : function(e, el)
8095     {
8096         var cell = Roo.get(el);
8097         
8098         if(!cell){
8099             return;
8100         }
8101         
8102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8103             cell = cell.findParent('td', false, true);
8104         }
8105         
8106         var row = cell.findParent('tr', false, true);
8107         var cellIndex = cell.dom.cellIndex;
8108         var rowIndex = row.dom.rowIndex - 1; // start from 0
8109         
8110         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8111         
8112     },
8113     
8114     onClick : function(e, el)
8115     {
8116         var cell = Roo.get(el);
8117         
8118         if(!cell || (!this.cellSelection && !this.rowSelection)){
8119             return;
8120         }
8121         
8122         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8123             cell = cell.findParent('td', false, true);
8124         }
8125         
8126         if(!cell || typeof(cell) == 'undefined'){
8127             return;
8128         }
8129         
8130         var row = cell.findParent('tr', false, true);
8131         
8132         if(!row || typeof(row) == 'undefined'){
8133             return;
8134         }
8135         
8136         var cellIndex = cell.dom.cellIndex;
8137         var rowIndex = this.getRowIndex(row);
8138         
8139         // why??? - should these not be based on SelectionModel?
8140         if(this.cellSelection){
8141             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8142         }
8143         
8144         if(this.rowSelection){
8145             this.fireEvent('rowclick', this, row, rowIndex, e);
8146         }
8147         
8148         
8149     },
8150         
8151     onDblClick : function(e,el)
8152     {
8153         var cell = Roo.get(el);
8154         
8155         if(!cell || (!this.cellSelection && !this.rowSelection)){
8156             return;
8157         }
8158         
8159         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160             cell = cell.findParent('td', false, true);
8161         }
8162         
8163         if(!cell || typeof(cell) == 'undefined'){
8164             return;
8165         }
8166         
8167         var row = cell.findParent('tr', false, true);
8168         
8169         if(!row || typeof(row) == 'undefined'){
8170             return;
8171         }
8172         
8173         var cellIndex = cell.dom.cellIndex;
8174         var rowIndex = this.getRowIndex(row);
8175         
8176         if(this.cellSelection){
8177             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8178         }
8179         
8180         if(this.rowSelection){
8181             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8182         }
8183     },
8184     
8185     sort : function(e,el)
8186     {
8187         var col = Roo.get(el);
8188         
8189         if(!col.hasClass('sortable')){
8190             return;
8191         }
8192         
8193         var sort = col.attr('sort');
8194         var dir = 'ASC';
8195         
8196         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8197             dir = 'DESC';
8198         }
8199         
8200         this.store.sortInfo = {field : sort, direction : dir};
8201         
8202         if (this.footer) {
8203             Roo.log("calling footer first");
8204             this.footer.onClick('first');
8205         } else {
8206         
8207             this.store.load({ params : { start : 0 } });
8208         }
8209     },
8210     
8211     renderHeader : function()
8212     {
8213         var header = {
8214             tag: 'thead',
8215             cn : []
8216         };
8217         
8218         var cm = this.cm;
8219         this.totalWidth = 0;
8220         
8221         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8222             
8223             var config = cm.config[i];
8224             
8225             var c = {
8226                 tag: 'th',
8227                 cls : 'x-hcol-' + i,
8228                 style : '',
8229                 html: cm.getColumnHeader(i)
8230             };
8231             
8232             var hh = '';
8233             
8234             if(typeof(config.sortable) != 'undefined' && config.sortable){
8235                 c.cls = 'sortable';
8236                 c.html = '<i class="glyphicon"></i>' + c.html;
8237             }
8238             
8239             // could use BS4 hidden-..-down 
8240             
8241             if(typeof(config.lgHeader) != 'undefined'){
8242                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8243             }
8244             
8245             if(typeof(config.mdHeader) != 'undefined'){
8246                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8247             }
8248             
8249             if(typeof(config.smHeader) != 'undefined'){
8250                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8251             }
8252             
8253             if(typeof(config.xsHeader) != 'undefined'){
8254                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8255             }
8256             
8257             if(hh.length){
8258                 c.html = hh;
8259             }
8260             
8261             if(typeof(config.tooltip) != 'undefined'){
8262                 c.tooltip = config.tooltip;
8263             }
8264             
8265             if(typeof(config.colspan) != 'undefined'){
8266                 c.colspan = config.colspan;
8267             }
8268             
8269             if(typeof(config.hidden) != 'undefined' && config.hidden){
8270                 c.style += ' display:none;';
8271             }
8272             
8273             if(typeof(config.dataIndex) != 'undefined'){
8274                 c.sort = config.dataIndex;
8275             }
8276             
8277            
8278             
8279             if(typeof(config.align) != 'undefined' && config.align.length){
8280                 c.style += ' text-align:' + config.align + ';';
8281             }
8282             
8283             if(typeof(config.width) != 'undefined'){
8284                 c.style += ' width:' + config.width + 'px;';
8285                 this.totalWidth += config.width;
8286             } else {
8287                 this.totalWidth += 100; // assume minimum of 100 per column?
8288             }
8289             
8290             if(typeof(config.cls) != 'undefined'){
8291                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8292             }
8293             
8294             ['xs','sm','md','lg'].map(function(size){
8295                 
8296                 if(typeof(config[size]) == 'undefined'){
8297                     return;
8298                 }
8299                  
8300                 if (!config[size]) { // 0 = hidden
8301                     // BS 4 '0' is treated as hide that column and below.
8302                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8303                     return;
8304                 }
8305                 
8306                 c.cls += ' col-' + size + '-' + config[size] + (
8307                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8308                 );
8309                 
8310                 
8311             });
8312             
8313             header.cn.push(c)
8314         }
8315         
8316         return header;
8317     },
8318     
8319     renderBody : function()
8320     {
8321         var body = {
8322             tag: 'tbody',
8323             cn : [
8324                 {
8325                     tag: 'tr',
8326                     cn : [
8327                         {
8328                             tag : 'td',
8329                             colspan :  this.cm.getColumnCount()
8330                         }
8331                     ]
8332                 }
8333             ]
8334         };
8335         
8336         return body;
8337     },
8338     
8339     renderFooter : function()
8340     {
8341         var footer = {
8342             tag: 'tfoot',
8343             cn : [
8344                 {
8345                     tag: 'tr',
8346                     cn : [
8347                         {
8348                             tag : 'td',
8349                             colspan :  this.cm.getColumnCount()
8350                         }
8351                     ]
8352                 }
8353             ]
8354         };
8355         
8356         return footer;
8357     },
8358     
8359     
8360     
8361     onLoad : function()
8362     {
8363 //        Roo.log('ds onload');
8364         this.clear();
8365         
8366         var _this = this;
8367         var cm = this.cm;
8368         var ds = this.store;
8369         
8370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8371             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8372             if (_this.store.sortInfo) {
8373                     
8374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8375                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8376                 }
8377                 
8378                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8379                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8380                 }
8381             }
8382         });
8383         
8384         var tbody =  this.mainBody;
8385               
8386         if(ds.getCount() > 0){
8387             ds.data.each(function(d,rowIndex){
8388                 var row =  this.renderRow(cm, ds, rowIndex);
8389                 
8390                 tbody.createChild(row);
8391                 
8392                 var _this = this;
8393                 
8394                 if(row.cellObjects.length){
8395                     Roo.each(row.cellObjects, function(r){
8396                         _this.renderCellObject(r);
8397                     })
8398                 }
8399                 
8400             }, this);
8401         }
8402         
8403         var tfoot = this.el.select('tfoot', true).first();
8404         
8405         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8406             
8407             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8408             
8409             var total = this.ds.getTotalCount();
8410             
8411             if(this.footer.pageSize < total){
8412                 this.mainFoot.show();
8413             }
8414         }
8415         
8416         Roo.each(this.el.select('tbody td', true).elements, function(e){
8417             e.on('mouseover', _this.onMouseover, _this);
8418         });
8419         
8420         Roo.each(this.el.select('tbody td', true).elements, function(e){
8421             e.on('mouseout', _this.onMouseout, _this);
8422         });
8423         this.fireEvent('rowsrendered', this);
8424         
8425         this.autoSize();
8426     },
8427     
8428     
8429     onUpdate : function(ds,record)
8430     {
8431         this.refreshRow(record);
8432         this.autoSize();
8433     },
8434     
8435     onRemove : function(ds, record, index, isUpdate){
8436         if(isUpdate !== true){
8437             this.fireEvent("beforerowremoved", this, index, record);
8438         }
8439         var bt = this.mainBody.dom;
8440         
8441         var rows = this.el.select('tbody > tr', true).elements;
8442         
8443         if(typeof(rows[index]) != 'undefined'){
8444             bt.removeChild(rows[index].dom);
8445         }
8446         
8447 //        if(bt.rows[index]){
8448 //            bt.removeChild(bt.rows[index]);
8449 //        }
8450         
8451         if(isUpdate !== true){
8452             //this.stripeRows(index);
8453             //this.syncRowHeights(index, index);
8454             //this.layout();
8455             this.fireEvent("rowremoved", this, index, record);
8456         }
8457     },
8458     
8459     onAdd : function(ds, records, rowIndex)
8460     {
8461         //Roo.log('on Add called');
8462         // - note this does not handle multiple adding very well..
8463         var bt = this.mainBody.dom;
8464         for (var i =0 ; i < records.length;i++) {
8465             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8466             //Roo.log(records[i]);
8467             //Roo.log(this.store.getAt(rowIndex+i));
8468             this.insertRow(this.store, rowIndex + i, false);
8469             return;
8470         }
8471         
8472     },
8473     
8474     
8475     refreshRow : function(record){
8476         var ds = this.store, index;
8477         if(typeof record == 'number'){
8478             index = record;
8479             record = ds.getAt(index);
8480         }else{
8481             index = ds.indexOf(record);
8482             if (index < 0) {
8483                 return; // should not happen - but seems to 
8484             }
8485         }
8486         this.insertRow(ds, index, true);
8487         this.autoSize();
8488         this.onRemove(ds, record, index+1, true);
8489         this.autoSize();
8490         //this.syncRowHeights(index, index);
8491         //this.layout();
8492         this.fireEvent("rowupdated", this, index, record);
8493     },
8494     
8495     insertRow : function(dm, rowIndex, isUpdate){
8496         
8497         if(!isUpdate){
8498             this.fireEvent("beforerowsinserted", this, rowIndex);
8499         }
8500             //var s = this.getScrollState();
8501         var row = this.renderRow(this.cm, this.store, rowIndex);
8502         // insert before rowIndex..
8503         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8504         
8505         var _this = this;
8506                 
8507         if(row.cellObjects.length){
8508             Roo.each(row.cellObjects, function(r){
8509                 _this.renderCellObject(r);
8510             })
8511         }
8512             
8513         if(!isUpdate){
8514             this.fireEvent("rowsinserted", this, rowIndex);
8515             //this.syncRowHeights(firstRow, lastRow);
8516             //this.stripeRows(firstRow);
8517             //this.layout();
8518         }
8519         
8520     },
8521     
8522     
8523     getRowDom : function(rowIndex)
8524     {
8525         var rows = this.el.select('tbody > tr', true).elements;
8526         
8527         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8528         
8529     },
8530     // returns the object tree for a tr..
8531   
8532     
8533     renderRow : function(cm, ds, rowIndex) 
8534     {
8535         var d = ds.getAt(rowIndex);
8536         
8537         var row = {
8538             tag : 'tr',
8539             cls : 'x-row-' + rowIndex,
8540             cn : []
8541         };
8542             
8543         var cellObjects = [];
8544         
8545         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8546             var config = cm.config[i];
8547             
8548             var renderer = cm.getRenderer(i);
8549             var value = '';
8550             var id = false;
8551             
8552             if(typeof(renderer) !== 'undefined'){
8553                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8554             }
8555             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8556             // and are rendered into the cells after the row is rendered - using the id for the element.
8557             
8558             if(typeof(value) === 'object'){
8559                 id = Roo.id();
8560                 cellObjects.push({
8561                     container : id,
8562                     cfg : value 
8563                 })
8564             }
8565             
8566             var rowcfg = {
8567                 record: d,
8568                 rowIndex : rowIndex,
8569                 colIndex : i,
8570                 rowClass : ''
8571             };
8572
8573             this.fireEvent('rowclass', this, rowcfg);
8574             
8575             var td = {
8576                 tag: 'td',
8577                 cls : rowcfg.rowClass + ' x-col-' + i,
8578                 style: '',
8579                 html: (typeof(value) === 'object') ? '' : value
8580             };
8581             
8582             if (id) {
8583                 td.id = id;
8584             }
8585             
8586             if(typeof(config.colspan) != 'undefined'){
8587                 td.colspan = config.colspan;
8588             }
8589             
8590             if(typeof(config.hidden) != 'undefined' && config.hidden){
8591                 td.style += ' display:none;';
8592             }
8593             
8594             if(typeof(config.align) != 'undefined' && config.align.length){
8595                 td.style += ' text-align:' + config.align + ';';
8596             }
8597             if(typeof(config.valign) != 'undefined' && config.valign.length){
8598                 td.style += ' vertical-align:' + config.valign + ';';
8599             }
8600             
8601             if(typeof(config.width) != 'undefined'){
8602                 td.style += ' width:' +  config.width + 'px;';
8603             }
8604             
8605             if(typeof(config.cursor) != 'undefined'){
8606                 td.style += ' cursor:' +  config.cursor + ';';
8607             }
8608             
8609             if(typeof(config.cls) != 'undefined'){
8610                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8611             }
8612             
8613             ['xs','sm','md','lg'].map(function(size){
8614                 
8615                 if(typeof(config[size]) == 'undefined'){
8616                     return;
8617                 }
8618                 
8619                 
8620                   
8621                 if (!config[size]) { // 0 = hidden
8622                     // BS 4 '0' is treated as hide that column and below.
8623                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8624                     return;
8625                 }
8626                 
8627                 td.cls += ' col-' + size + '-' + config[size] + (
8628                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8629                 );
8630                  
8631
8632             });
8633             
8634             row.cn.push(td);
8635            
8636         }
8637         
8638         row.cellObjects = cellObjects;
8639         
8640         return row;
8641           
8642     },
8643     
8644     
8645     
8646     onBeforeLoad : function()
8647     {
8648         
8649     },
8650      /**
8651      * Remove all rows
8652      */
8653     clear : function()
8654     {
8655         this.el.select('tbody', true).first().dom.innerHTML = '';
8656     },
8657     /**
8658      * Show or hide a row.
8659      * @param {Number} rowIndex to show or hide
8660      * @param {Boolean} state hide
8661      */
8662     setRowVisibility : function(rowIndex, state)
8663     {
8664         var bt = this.mainBody.dom;
8665         
8666         var rows = this.el.select('tbody > tr', true).elements;
8667         
8668         if(typeof(rows[rowIndex]) == 'undefined'){
8669             return;
8670         }
8671         rows[rowIndex].dom.style.display = state ? '' : 'none';
8672     },
8673     
8674     
8675     getSelectionModel : function(){
8676         if(!this.selModel){
8677             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8678         }
8679         return this.selModel;
8680     },
8681     /*
8682      * Render the Roo.bootstrap object from renderder
8683      */
8684     renderCellObject : function(r)
8685     {
8686         var _this = this;
8687         
8688         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8689         
8690         var t = r.cfg.render(r.container);
8691         
8692         if(r.cfg.cn){
8693             Roo.each(r.cfg.cn, function(c){
8694                 var child = {
8695                     container: t.getChildContainer(),
8696                     cfg: c
8697                 };
8698                 _this.renderCellObject(child);
8699             })
8700         }
8701     },
8702     
8703     getRowIndex : function(row)
8704     {
8705         var rowIndex = -1;
8706         
8707         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8708             if(el != row){
8709                 return;
8710             }
8711             
8712             rowIndex = index;
8713         });
8714         
8715         return rowIndex;
8716     },
8717      /**
8718      * Returns the grid's underlying element = used by panel.Grid
8719      * @return {Element} The element
8720      */
8721     getGridEl : function(){
8722         return this.el;
8723     },
8724      /**
8725      * Forces a resize - used by panel.Grid
8726      * @return {Element} The element
8727      */
8728     autoSize : function()
8729     {
8730         //var ctr = Roo.get(this.container.dom.parentElement);
8731         var ctr = Roo.get(this.el.dom);
8732         
8733         var thd = this.getGridEl().select('thead',true).first();
8734         var tbd = this.getGridEl().select('tbody', true).first();
8735         var tfd = this.getGridEl().select('tfoot', true).first();
8736         
8737         var cw = ctr.getWidth();
8738         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8739         
8740         if (tbd) {
8741             
8742             tbd.setWidth(ctr.getWidth());
8743             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8744             // this needs fixing for various usage - currently only hydra job advers I think..
8745             //tdb.setHeight(
8746             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8747             //); 
8748             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8749             cw -= barsize;
8750         }
8751         cw = Math.max(cw, this.totalWidth);
8752         this.getGridEl().select('tbody tr',true).setWidth(cw);
8753         
8754         // resize 'expandable coloumn?
8755         
8756         return; // we doe not have a view in this design..
8757         
8758     },
8759     onBodyScroll: function()
8760     {
8761         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8762         if(this.mainHead){
8763             this.mainHead.setStyle({
8764                 'position' : 'relative',
8765                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8766             });
8767         }
8768         
8769         if(this.lazyLoad){
8770             
8771             var scrollHeight = this.mainBody.dom.scrollHeight;
8772             
8773             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8774             
8775             var height = this.mainBody.getHeight();
8776             
8777             if(scrollHeight - height == scrollTop) {
8778                 
8779                 var total = this.ds.getTotalCount();
8780                 
8781                 if(this.footer.cursor + this.footer.pageSize < total){
8782                     
8783                     this.footer.ds.load({
8784                         params : {
8785                             start : this.footer.cursor + this.footer.pageSize,
8786                             limit : this.footer.pageSize
8787                         },
8788                         add : true
8789                     });
8790                 }
8791             }
8792             
8793         }
8794     },
8795     
8796     onHeaderChange : function()
8797     {
8798         var header = this.renderHeader();
8799         var table = this.el.select('table', true).first();
8800         
8801         this.mainHead.remove();
8802         this.mainHead = table.createChild(header, this.mainBody, false);
8803     },
8804     
8805     onHiddenChange : function(colModel, colIndex, hidden)
8806     {
8807         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8808         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8809         
8810         this.CSS.updateRule(thSelector, "display", "");
8811         this.CSS.updateRule(tdSelector, "display", "");
8812         
8813         if(hidden){
8814             this.CSS.updateRule(thSelector, "display", "none");
8815             this.CSS.updateRule(tdSelector, "display", "none");
8816         }
8817         
8818         this.onHeaderChange();
8819         this.onLoad();
8820     },
8821     
8822     setColumnWidth: function(col_index, width)
8823     {
8824         // width = "md-2 xs-2..."
8825         if(!this.colModel.config[col_index]) {
8826             return;
8827         }
8828         
8829         var w = width.split(" ");
8830         
8831         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8832         
8833         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8834         
8835         
8836         for(var j = 0; j < w.length; j++) {
8837             
8838             if(!w[j]) {
8839                 continue;
8840             }
8841             
8842             var size_cls = w[j].split("-");
8843             
8844             if(!Number.isInteger(size_cls[1] * 1)) {
8845                 continue;
8846             }
8847             
8848             if(!this.colModel.config[col_index][size_cls[0]]) {
8849                 continue;
8850             }
8851             
8852             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8853                 continue;
8854             }
8855             
8856             h_row[0].classList.replace(
8857                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8858                 "col-"+size_cls[0]+"-"+size_cls[1]
8859             );
8860             
8861             for(var i = 0; i < rows.length; i++) {
8862                 
8863                 var size_cls = w[j].split("-");
8864                 
8865                 if(!Number.isInteger(size_cls[1] * 1)) {
8866                     continue;
8867                 }
8868                 
8869                 if(!this.colModel.config[col_index][size_cls[0]]) {
8870                     continue;
8871                 }
8872                 
8873                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8874                     continue;
8875                 }
8876                 
8877                 rows[i].classList.replace(
8878                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8879                     "col-"+size_cls[0]+"-"+size_cls[1]
8880                 );
8881             }
8882             
8883             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8884         }
8885     }
8886 });
8887
8888  
8889
8890  /*
8891  * - LGPL
8892  *
8893  * table cell
8894  * 
8895  */
8896
8897 /**
8898  * @class Roo.bootstrap.TableCell
8899  * @extends Roo.bootstrap.Component
8900  * Bootstrap TableCell class
8901  * @cfg {String} html cell contain text
8902  * @cfg {String} cls cell class
8903  * @cfg {String} tag cell tag (td|th) default td
8904  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8905  * @cfg {String} align Aligns the content in a cell
8906  * @cfg {String} axis Categorizes cells
8907  * @cfg {String} bgcolor Specifies the background color of a cell
8908  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8909  * @cfg {Number} colspan Specifies the number of columns a cell should span
8910  * @cfg {String} headers Specifies one or more header cells a cell is related to
8911  * @cfg {Number} height Sets the height of a cell
8912  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8913  * @cfg {Number} rowspan Sets the number of rows a cell should span
8914  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8915  * @cfg {String} valign Vertical aligns the content in a cell
8916  * @cfg {Number} width Specifies the width of a cell
8917  * 
8918  * @constructor
8919  * Create a new TableCell
8920  * @param {Object} config The config object
8921  */
8922
8923 Roo.bootstrap.TableCell = function(config){
8924     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8925 };
8926
8927 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8928     
8929     html: false,
8930     cls: false,
8931     tag: false,
8932     abbr: false,
8933     align: false,
8934     axis: false,
8935     bgcolor: false,
8936     charoff: false,
8937     colspan: false,
8938     headers: false,
8939     height: false,
8940     nowrap: false,
8941     rowspan: false,
8942     scope: false,
8943     valign: false,
8944     width: false,
8945     
8946     
8947     getAutoCreate : function(){
8948         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8949         
8950         cfg = {
8951             tag: 'td'
8952         };
8953         
8954         if(this.tag){
8955             cfg.tag = this.tag;
8956         }
8957         
8958         if (this.html) {
8959             cfg.html=this.html
8960         }
8961         if (this.cls) {
8962             cfg.cls=this.cls
8963         }
8964         if (this.abbr) {
8965             cfg.abbr=this.abbr
8966         }
8967         if (this.align) {
8968             cfg.align=this.align
8969         }
8970         if (this.axis) {
8971             cfg.axis=this.axis
8972         }
8973         if (this.bgcolor) {
8974             cfg.bgcolor=this.bgcolor
8975         }
8976         if (this.charoff) {
8977             cfg.charoff=this.charoff
8978         }
8979         if (this.colspan) {
8980             cfg.colspan=this.colspan
8981         }
8982         if (this.headers) {
8983             cfg.headers=this.headers
8984         }
8985         if (this.height) {
8986             cfg.height=this.height
8987         }
8988         if (this.nowrap) {
8989             cfg.nowrap=this.nowrap
8990         }
8991         if (this.rowspan) {
8992             cfg.rowspan=this.rowspan
8993         }
8994         if (this.scope) {
8995             cfg.scope=this.scope
8996         }
8997         if (this.valign) {
8998             cfg.valign=this.valign
8999         }
9000         if (this.width) {
9001             cfg.width=this.width
9002         }
9003         
9004         
9005         return cfg;
9006     }
9007    
9008 });
9009
9010  
9011
9012  /*
9013  * - LGPL
9014  *
9015  * table row
9016  * 
9017  */
9018
9019 /**
9020  * @class Roo.bootstrap.TableRow
9021  * @extends Roo.bootstrap.Component
9022  * Bootstrap TableRow class
9023  * @cfg {String} cls row class
9024  * @cfg {String} align Aligns the content in a table row
9025  * @cfg {String} bgcolor Specifies a background color for a table row
9026  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9027  * @cfg {String} valign Vertical aligns the content in a table row
9028  * 
9029  * @constructor
9030  * Create a new TableRow
9031  * @param {Object} config The config object
9032  */
9033
9034 Roo.bootstrap.TableRow = function(config){
9035     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9036 };
9037
9038 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9039     
9040     cls: false,
9041     align: false,
9042     bgcolor: false,
9043     charoff: false,
9044     valign: false,
9045     
9046     getAutoCreate : function(){
9047         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9048         
9049         cfg = {
9050             tag: 'tr'
9051         };
9052             
9053         if(this.cls){
9054             cfg.cls = this.cls;
9055         }
9056         if(this.align){
9057             cfg.align = this.align;
9058         }
9059         if(this.bgcolor){
9060             cfg.bgcolor = this.bgcolor;
9061         }
9062         if(this.charoff){
9063             cfg.charoff = this.charoff;
9064         }
9065         if(this.valign){
9066             cfg.valign = this.valign;
9067         }
9068         
9069         return cfg;
9070     }
9071    
9072 });
9073
9074  
9075
9076  /*
9077  * - LGPL
9078  *
9079  * table body
9080  * 
9081  */
9082
9083 /**
9084  * @class Roo.bootstrap.TableBody
9085  * @extends Roo.bootstrap.Component
9086  * Bootstrap TableBody class
9087  * @cfg {String} cls element class
9088  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9089  * @cfg {String} align Aligns the content inside the element
9090  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9091  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9092  * 
9093  * @constructor
9094  * Create a new TableBody
9095  * @param {Object} config The config object
9096  */
9097
9098 Roo.bootstrap.TableBody = function(config){
9099     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9100 };
9101
9102 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9103     
9104     cls: false,
9105     tag: false,
9106     align: false,
9107     charoff: false,
9108     valign: false,
9109     
9110     getAutoCreate : function(){
9111         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9112         
9113         cfg = {
9114             tag: 'tbody'
9115         };
9116             
9117         if (this.cls) {
9118             cfg.cls=this.cls
9119         }
9120         if(this.tag){
9121             cfg.tag = this.tag;
9122         }
9123         
9124         if(this.align){
9125             cfg.align = this.align;
9126         }
9127         if(this.charoff){
9128             cfg.charoff = this.charoff;
9129         }
9130         if(this.valign){
9131             cfg.valign = this.valign;
9132         }
9133         
9134         return cfg;
9135     }
9136     
9137     
9138 //    initEvents : function()
9139 //    {
9140 //        
9141 //        if(!this.store){
9142 //            return;
9143 //        }
9144 //        
9145 //        this.store = Roo.factory(this.store, Roo.data);
9146 //        this.store.on('load', this.onLoad, this);
9147 //        
9148 //        this.store.load();
9149 //        
9150 //    },
9151 //    
9152 //    onLoad: function () 
9153 //    {   
9154 //        this.fireEvent('load', this);
9155 //    }
9156 //    
9157 //   
9158 });
9159
9160  
9161
9162  /*
9163  * Based on:
9164  * Ext JS Library 1.1.1
9165  * Copyright(c) 2006-2007, Ext JS, LLC.
9166  *
9167  * Originally Released Under LGPL - original licence link has changed is not relivant.
9168  *
9169  * Fork - LGPL
9170  * <script type="text/javascript">
9171  */
9172
9173 // as we use this in bootstrap.
9174 Roo.namespace('Roo.form');
9175  /**
9176  * @class Roo.form.Action
9177  * Internal Class used to handle form actions
9178  * @constructor
9179  * @param {Roo.form.BasicForm} el The form element or its id
9180  * @param {Object} config Configuration options
9181  */
9182
9183  
9184  
9185 // define the action interface
9186 Roo.form.Action = function(form, options){
9187     this.form = form;
9188     this.options = options || {};
9189 };
9190 /**
9191  * Client Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.CLIENT_INVALID = 'client';
9195 /**
9196  * Server Validation Failed
9197  * @const 
9198  */
9199 Roo.form.Action.SERVER_INVALID = 'server';
9200  /**
9201  * Connect to Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.CONNECT_FAILURE = 'connect';
9205 /**
9206  * Reading Data from Server Failed
9207  * @const 
9208  */
9209 Roo.form.Action.LOAD_FAILURE = 'load';
9210
9211 Roo.form.Action.prototype = {
9212     type : 'default',
9213     failureType : undefined,
9214     response : undefined,
9215     result : undefined,
9216
9217     // interface method
9218     run : function(options){
9219
9220     },
9221
9222     // interface method
9223     success : function(response){
9224
9225     },
9226
9227     // interface method
9228     handleResponse : function(response){
9229
9230     },
9231
9232     // default connection failure
9233     failure : function(response){
9234         
9235         this.response = response;
9236         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9237         this.form.afterAction(this, false);
9238     },
9239
9240     processResponse : function(response){
9241         this.response = response;
9242         if(!response.responseText){
9243             return true;
9244         }
9245         this.result = this.handleResponse(response);
9246         return this.result;
9247     },
9248
9249     // utility functions used internally
9250     getUrl : function(appendParams){
9251         var url = this.options.url || this.form.url || this.form.el.dom.action;
9252         if(appendParams){
9253             var p = this.getParams();
9254             if(p){
9255                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9256             }
9257         }
9258         return url;
9259     },
9260
9261     getMethod : function(){
9262         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9263     },
9264
9265     getParams : function(){
9266         var bp = this.form.baseParams;
9267         var p = this.options.params;
9268         if(p){
9269             if(typeof p == "object"){
9270                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9271             }else if(typeof p == 'string' && bp){
9272                 p += '&' + Roo.urlEncode(bp);
9273             }
9274         }else if(bp){
9275             p = Roo.urlEncode(bp);
9276         }
9277         return p;
9278     },
9279
9280     createCallback : function(){
9281         return {
9282             success: this.success,
9283             failure: this.failure,
9284             scope: this,
9285             timeout: (this.form.timeout*1000),
9286             upload: this.form.fileUpload ? this.success : undefined
9287         };
9288     }
9289 };
9290
9291 Roo.form.Action.Submit = function(form, options){
9292     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9293 };
9294
9295 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9296     type : 'submit',
9297
9298     haveProgress : false,
9299     uploadComplete : false,
9300     
9301     // uploadProgress indicator.
9302     uploadProgress : function()
9303     {
9304         if (!this.form.progressUrl) {
9305             return;
9306         }
9307         
9308         if (!this.haveProgress) {
9309             Roo.MessageBox.progress("Uploading", "Uploading");
9310         }
9311         if (this.uploadComplete) {
9312            Roo.MessageBox.hide();
9313            return;
9314         }
9315         
9316         this.haveProgress = true;
9317    
9318         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9319         
9320         var c = new Roo.data.Connection();
9321         c.request({
9322             url : this.form.progressUrl,
9323             params: {
9324                 id : uid
9325             },
9326             method: 'GET',
9327             success : function(req){
9328                //console.log(data);
9329                 var rdata = false;
9330                 var edata;
9331                 try  {
9332                    rdata = Roo.decode(req.responseText)
9333                 } catch (e) {
9334                     Roo.log("Invalid data from server..");
9335                     Roo.log(edata);
9336                     return;
9337                 }
9338                 if (!rdata || !rdata.success) {
9339                     Roo.log(rdata);
9340                     Roo.MessageBox.alert(Roo.encode(rdata));
9341                     return;
9342                 }
9343                 var data = rdata.data;
9344                 
9345                 if (this.uploadComplete) {
9346                    Roo.MessageBox.hide();
9347                    return;
9348                 }
9349                    
9350                 if (data){
9351                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9352                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9353                     );
9354                 }
9355                 this.uploadProgress.defer(2000,this);
9356             },
9357        
9358             failure: function(data) {
9359                 Roo.log('progress url failed ');
9360                 Roo.log(data);
9361             },
9362             scope : this
9363         });
9364            
9365     },
9366     
9367     
9368     run : function()
9369     {
9370         // run get Values on the form, so it syncs any secondary forms.
9371         this.form.getValues();
9372         
9373         var o = this.options;
9374         var method = this.getMethod();
9375         var isPost = method == 'POST';
9376         if(o.clientValidation === false || this.form.isValid()){
9377             
9378             if (this.form.progressUrl) {
9379                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9380                     (new Date() * 1) + '' + Math.random());
9381                     
9382             } 
9383             
9384             
9385             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9386                 form:this.form.el.dom,
9387                 url:this.getUrl(!isPost),
9388                 method: method,
9389                 params:isPost ? this.getParams() : null,
9390                 isUpload: this.form.fileUpload,
9391                 formData : this.form.formData
9392             }));
9393             
9394             this.uploadProgress();
9395
9396         }else if (o.clientValidation !== false){ // client validation failed
9397             this.failureType = Roo.form.Action.CLIENT_INVALID;
9398             this.form.afterAction(this, false);
9399         }
9400     },
9401
9402     success : function(response)
9403     {
9404         this.uploadComplete= true;
9405         if (this.haveProgress) {
9406             Roo.MessageBox.hide();
9407         }
9408         
9409         
9410         var result = this.processResponse(response);
9411         if(result === true || result.success){
9412             this.form.afterAction(this, true);
9413             return;
9414         }
9415         if(result.errors){
9416             this.form.markInvalid(result.errors);
9417             this.failureType = Roo.form.Action.SERVER_INVALID;
9418         }
9419         this.form.afterAction(this, false);
9420     },
9421     failure : function(response)
9422     {
9423         this.uploadComplete= true;
9424         if (this.haveProgress) {
9425             Roo.MessageBox.hide();
9426         }
9427         
9428         this.response = response;
9429         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9430         this.form.afterAction(this, false);
9431     },
9432     
9433     handleResponse : function(response){
9434         if(this.form.errorReader){
9435             var rs = this.form.errorReader.read(response);
9436             var errors = [];
9437             if(rs.records){
9438                 for(var i = 0, len = rs.records.length; i < len; i++) {
9439                     var r = rs.records[i];
9440                     errors[i] = r.data;
9441                 }
9442             }
9443             if(errors.length < 1){
9444                 errors = null;
9445             }
9446             return {
9447                 success : rs.success,
9448                 errors : errors
9449             };
9450         }
9451         var ret = false;
9452         try {
9453             ret = Roo.decode(response.responseText);
9454         } catch (e) {
9455             ret = {
9456                 success: false,
9457                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9458                 errors : []
9459             };
9460         }
9461         return ret;
9462         
9463     }
9464 });
9465
9466
9467 Roo.form.Action.Load = function(form, options){
9468     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9469     this.reader = this.form.reader;
9470 };
9471
9472 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9473     type : 'load',
9474
9475     run : function(){
9476         
9477         Roo.Ajax.request(Roo.apply(
9478                 this.createCallback(), {
9479                     method:this.getMethod(),
9480                     url:this.getUrl(false),
9481                     params:this.getParams()
9482         }));
9483     },
9484
9485     success : function(response){
9486         
9487         var result = this.processResponse(response);
9488         if(result === true || !result.success || !result.data){
9489             this.failureType = Roo.form.Action.LOAD_FAILURE;
9490             this.form.afterAction(this, false);
9491             return;
9492         }
9493         this.form.clearInvalid();
9494         this.form.setValues(result.data);
9495         this.form.afterAction(this, true);
9496     },
9497
9498     handleResponse : function(response){
9499         if(this.form.reader){
9500             var rs = this.form.reader.read(response);
9501             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9502             return {
9503                 success : rs.success,
9504                 data : data
9505             };
9506         }
9507         return Roo.decode(response.responseText);
9508     }
9509 });
9510
9511 Roo.form.Action.ACTION_TYPES = {
9512     'load' : Roo.form.Action.Load,
9513     'submit' : Roo.form.Action.Submit
9514 };/*
9515  * - LGPL
9516  *
9517  * form
9518  *
9519  */
9520
9521 /**
9522  * @class Roo.bootstrap.Form
9523  * @extends Roo.bootstrap.Component
9524  * Bootstrap Form class
9525  * @cfg {String} method  GET | POST (default POST)
9526  * @cfg {String} labelAlign top | left (default top)
9527  * @cfg {String} align left  | right - for navbars
9528  * @cfg {Boolean} loadMask load mask when submit (default true)
9529
9530  *
9531  * @constructor
9532  * Create a new Form
9533  * @param {Object} config The config object
9534  */
9535
9536
9537 Roo.bootstrap.Form = function(config){
9538     
9539     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9540     
9541     Roo.bootstrap.Form.popover.apply();
9542     
9543     this.addEvents({
9544         /**
9545          * @event clientvalidation
9546          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9547          * @param {Form} this
9548          * @param {Boolean} valid true if the form has passed client-side validation
9549          */
9550         clientvalidation: true,
9551         /**
9552          * @event beforeaction
9553          * Fires before any action is performed. Return false to cancel the action.
9554          * @param {Form} this
9555          * @param {Action} action The action to be performed
9556          */
9557         beforeaction: true,
9558         /**
9559          * @event actionfailed
9560          * Fires when an action fails.
9561          * @param {Form} this
9562          * @param {Action} action The action that failed
9563          */
9564         actionfailed : true,
9565         /**
9566          * @event actioncomplete
9567          * Fires when an action is completed.
9568          * @param {Form} this
9569          * @param {Action} action The action that completed
9570          */
9571         actioncomplete : true
9572     });
9573 };
9574
9575 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9576
9577      /**
9578      * @cfg {String} method
9579      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9580      */
9581     method : 'POST',
9582     /**
9583      * @cfg {String} url
9584      * The URL to use for form actions if one isn't supplied in the action options.
9585      */
9586     /**
9587      * @cfg {Boolean} fileUpload
9588      * Set to true if this form is a file upload.
9589      */
9590
9591     /**
9592      * @cfg {Object} baseParams
9593      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9594      */
9595
9596     /**
9597      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9598      */
9599     timeout: 30,
9600     /**
9601      * @cfg {Sting} align (left|right) for navbar forms
9602      */
9603     align : 'left',
9604
9605     // private
9606     activeAction : null,
9607
9608     /**
9609      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9610      * element by passing it or its id or mask the form itself by passing in true.
9611      * @type Mixed
9612      */
9613     waitMsgTarget : false,
9614
9615     loadMask : true,
9616     
9617     /**
9618      * @cfg {Boolean} errorMask (true|false) default false
9619      */
9620     errorMask : false,
9621     
9622     /**
9623      * @cfg {Number} maskOffset Default 100
9624      */
9625     maskOffset : 100,
9626     
9627     /**
9628      * @cfg {Boolean} maskBody
9629      */
9630     maskBody : false,
9631
9632     getAutoCreate : function(){
9633
9634         var cfg = {
9635             tag: 'form',
9636             method : this.method || 'POST',
9637             id : this.id || Roo.id(),
9638             cls : ''
9639         };
9640         if (this.parent().xtype.match(/^Nav/)) {
9641             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9642
9643         }
9644
9645         if (this.labelAlign == 'left' ) {
9646             cfg.cls += ' form-horizontal';
9647         }
9648
9649
9650         return cfg;
9651     },
9652     initEvents : function()
9653     {
9654         this.el.on('submit', this.onSubmit, this);
9655         // this was added as random key presses on the form where triggering form submit.
9656         this.el.on('keypress', function(e) {
9657             if (e.getCharCode() != 13) {
9658                 return true;
9659             }
9660             // we might need to allow it for textareas.. and some other items.
9661             // check e.getTarget().
9662
9663             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9664                 return true;
9665             }
9666
9667             Roo.log("keypress blocked");
9668
9669             e.preventDefault();
9670             return false;
9671         });
9672         
9673     },
9674     // private
9675     onSubmit : function(e){
9676         e.stopEvent();
9677     },
9678
9679      /**
9680      * Returns true if client-side validation on the form is successful.
9681      * @return Boolean
9682      */
9683     isValid : function(){
9684         var items = this.getItems();
9685         var valid = true;
9686         var target = false;
9687         
9688         items.each(function(f){
9689             
9690             if(f.validate()){
9691                 return;
9692             }
9693             
9694             Roo.log('invalid field: ' + f.name);
9695             
9696             valid = false;
9697
9698             if(!target && f.el.isVisible(true)){
9699                 target = f;
9700             }
9701            
9702         });
9703         
9704         if(this.errorMask && !valid){
9705             Roo.bootstrap.Form.popover.mask(this, target);
9706         }
9707         
9708         return valid;
9709     },
9710     
9711     /**
9712      * Returns true if any fields in this form have changed since their original load.
9713      * @return Boolean
9714      */
9715     isDirty : function(){
9716         var dirty = false;
9717         var items = this.getItems();
9718         items.each(function(f){
9719            if(f.isDirty()){
9720                dirty = true;
9721                return false;
9722            }
9723            return true;
9724         });
9725         return dirty;
9726     },
9727      /**
9728      * Performs a predefined action (submit or load) or custom actions you define on this form.
9729      * @param {String} actionName The name of the action type
9730      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9731      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9732      * accept other config options):
9733      * <pre>
9734 Property          Type             Description
9735 ----------------  ---------------  ----------------------------------------------------------------------------------
9736 url               String           The url for the action (defaults to the form's url)
9737 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9738 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9739 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9740                                    validate the form on the client (defaults to false)
9741      * </pre>
9742      * @return {BasicForm} this
9743      */
9744     doAction : function(action, options){
9745         if(typeof action == 'string'){
9746             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9747         }
9748         if(this.fireEvent('beforeaction', this, action) !== false){
9749             this.beforeAction(action);
9750             action.run.defer(100, action);
9751         }
9752         return this;
9753     },
9754
9755     // private
9756     beforeAction : function(action){
9757         var o = action.options;
9758         
9759         if(this.loadMask){
9760             
9761             if(this.maskBody){
9762                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9763             } else {
9764                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9765             }
9766         }
9767         // not really supported yet.. ??
9768
9769         //if(this.waitMsgTarget === true){
9770         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771         //}else if(this.waitMsgTarget){
9772         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9773         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else {
9775         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9776        // }
9777
9778     },
9779
9780     // private
9781     afterAction : function(action, success){
9782         this.activeAction = null;
9783         var o = action.options;
9784
9785         if(this.loadMask){
9786             
9787             if(this.maskBody){
9788                 Roo.get(document.body).unmask();
9789             } else {
9790                 this.el.unmask();
9791             }
9792         }
9793         
9794         //if(this.waitMsgTarget === true){
9795 //            this.el.unmask();
9796         //}else if(this.waitMsgTarget){
9797         //    this.waitMsgTarget.unmask();
9798         //}else{
9799         //    Roo.MessageBox.updateProgress(1);
9800         //    Roo.MessageBox.hide();
9801        // }
9802         //
9803         if(success){
9804             if(o.reset){
9805                 this.reset();
9806             }
9807             Roo.callback(o.success, o.scope, [this, action]);
9808             this.fireEvent('actioncomplete', this, action);
9809
9810         }else{
9811
9812             // failure condition..
9813             // we have a scenario where updates need confirming.
9814             // eg. if a locking scenario exists..
9815             // we look for { errors : { needs_confirm : true }} in the response.
9816             if (
9817                 (typeof(action.result) != 'undefined')  &&
9818                 (typeof(action.result.errors) != 'undefined')  &&
9819                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9820            ){
9821                 var _t = this;
9822                 Roo.log("not supported yet");
9823                  /*
9824
9825                 Roo.MessageBox.confirm(
9826                     "Change requires confirmation",
9827                     action.result.errorMsg,
9828                     function(r) {
9829                         if (r != 'yes') {
9830                             return;
9831                         }
9832                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9833                     }
9834
9835                 );
9836                 */
9837
9838
9839                 return;
9840             }
9841
9842             Roo.callback(o.failure, o.scope, [this, action]);
9843             // show an error message if no failed handler is set..
9844             if (!this.hasListener('actionfailed')) {
9845                 Roo.log("need to add dialog support");
9846                 /*
9847                 Roo.MessageBox.alert("Error",
9848                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9849                         action.result.errorMsg :
9850                         "Saving Failed, please check your entries or try again"
9851                 );
9852                 */
9853             }
9854
9855             this.fireEvent('actionfailed', this, action);
9856         }
9857
9858     },
9859     /**
9860      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9861      * @param {String} id The value to search for
9862      * @return Field
9863      */
9864     findField : function(id){
9865         var items = this.getItems();
9866         var field = items.get(id);
9867         if(!field){
9868              items.each(function(f){
9869                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9870                     field = f;
9871                     return false;
9872                 }
9873                 return true;
9874             });
9875         }
9876         return field || null;
9877     },
9878      /**
9879      * Mark fields in this form invalid in bulk.
9880      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9881      * @return {BasicForm} this
9882      */
9883     markInvalid : function(errors){
9884         if(errors instanceof Array){
9885             for(var i = 0, len = errors.length; i < len; i++){
9886                 var fieldError = errors[i];
9887                 var f = this.findField(fieldError.id);
9888                 if(f){
9889                     f.markInvalid(fieldError.msg);
9890                 }
9891             }
9892         }else{
9893             var field, id;
9894             for(id in errors){
9895                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9896                     field.markInvalid(errors[id]);
9897                 }
9898             }
9899         }
9900         //Roo.each(this.childForms || [], function (f) {
9901         //    f.markInvalid(errors);
9902         //});
9903
9904         return this;
9905     },
9906
9907     /**
9908      * Set values for fields in this form in bulk.
9909      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9910      * @return {BasicForm} this
9911      */
9912     setValues : function(values){
9913         if(values instanceof Array){ // array of objects
9914             for(var i = 0, len = values.length; i < len; i++){
9915                 var v = values[i];
9916                 var f = this.findField(v.id);
9917                 if(f){
9918                     f.setValue(v.value);
9919                     if(this.trackResetOnLoad){
9920                         f.originalValue = f.getValue();
9921                     }
9922                 }
9923             }
9924         }else{ // object hash
9925             var field, id;
9926             for(id in values){
9927                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9928
9929                     if (field.setFromData &&
9930                         field.valueField &&
9931                         field.displayField &&
9932                         // combos' with local stores can
9933                         // be queried via setValue()
9934                         // to set their value..
9935                         (field.store && !field.store.isLocal)
9936                         ) {
9937                         // it's a combo
9938                         var sd = { };
9939                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9940                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9941                         field.setFromData(sd);
9942
9943                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9944                         
9945                         field.setFromData(values);
9946                         
9947                     } else {
9948                         field.setValue(values[id]);
9949                     }
9950
9951
9952                     if(this.trackResetOnLoad){
9953                         field.originalValue = field.getValue();
9954                     }
9955                 }
9956             }
9957         }
9958
9959         //Roo.each(this.childForms || [], function (f) {
9960         //    f.setValues(values);
9961         //});
9962
9963         return this;
9964     },
9965
9966     /**
9967      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9968      * they are returned as an array.
9969      * @param {Boolean} asString
9970      * @return {Object}
9971      */
9972     getValues : function(asString){
9973         //if (this.childForms) {
9974             // copy values from the child forms
9975         //    Roo.each(this.childForms, function (f) {
9976         //        this.setValues(f.getValues());
9977         //    }, this);
9978         //}
9979
9980
9981
9982         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9983         if(asString === true){
9984             return fs;
9985         }
9986         return Roo.urlDecode(fs);
9987     },
9988
9989     /**
9990      * Returns the fields in this form as an object with key/value pairs.
9991      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9992      * @return {Object}
9993      */
9994     getFieldValues : function(with_hidden)
9995     {
9996         var items = this.getItems();
9997         var ret = {};
9998         items.each(function(f){
9999             
10000             if (!f.getName()) {
10001                 return;
10002             }
10003             
10004             var v = f.getValue();
10005             
10006             if (f.inputType =='radio') {
10007                 if (typeof(ret[f.getName()]) == 'undefined') {
10008                     ret[f.getName()] = ''; // empty..
10009                 }
10010
10011                 if (!f.el.dom.checked) {
10012                     return;
10013
10014                 }
10015                 v = f.el.dom.value;
10016
10017             }
10018             
10019             if(f.xtype == 'MoneyField'){
10020                 ret[f.currencyName] = f.getCurrency();
10021             }
10022
10023             // not sure if this supported any more..
10024             if ((typeof(v) == 'object') && f.getRawValue) {
10025                 v = f.getRawValue() ; // dates..
10026             }
10027             // combo boxes where name != hiddenName...
10028             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10029                 ret[f.name] = f.getRawValue();
10030             }
10031             ret[f.getName()] = v;
10032         });
10033
10034         return ret;
10035     },
10036
10037     /**
10038      * Clears all invalid messages in this form.
10039      * @return {BasicForm} this
10040      */
10041     clearInvalid : function(){
10042         var items = this.getItems();
10043
10044         items.each(function(f){
10045            f.clearInvalid();
10046         });
10047
10048         return this;
10049     },
10050
10051     /**
10052      * Resets this form.
10053      * @return {BasicForm} this
10054      */
10055     reset : function(){
10056         var items = this.getItems();
10057         items.each(function(f){
10058             f.reset();
10059         });
10060
10061         Roo.each(this.childForms || [], function (f) {
10062             f.reset();
10063         });
10064
10065
10066         return this;
10067     },
10068     
10069     getItems : function()
10070     {
10071         var r=new Roo.util.MixedCollection(false, function(o){
10072             return o.id || (o.id = Roo.id());
10073         });
10074         var iter = function(el) {
10075             if (el.inputEl) {
10076                 r.add(el);
10077             }
10078             if (!el.items) {
10079                 return;
10080             }
10081             Roo.each(el.items,function(e) {
10082                 iter(e);
10083             });
10084         };
10085
10086         iter(this);
10087         return r;
10088     },
10089     
10090     hideFields : function(items)
10091     {
10092         Roo.each(items, function(i){
10093             
10094             var f = this.findField(i);
10095             
10096             if(!f){
10097                 return;
10098             }
10099             
10100             f.hide();
10101             
10102         }, this);
10103     },
10104     
10105     showFields : function(items)
10106     {
10107         Roo.each(items, function(i){
10108             
10109             var f = this.findField(i);
10110             
10111             if(!f){
10112                 return;
10113             }
10114             
10115             f.show();
10116             
10117         }, this);
10118     }
10119
10120 });
10121
10122 Roo.apply(Roo.bootstrap.Form, {
10123     
10124     popover : {
10125         
10126         padding : 5,
10127         
10128         isApplied : false,
10129         
10130         isMasked : false,
10131         
10132         form : false,
10133         
10134         target : false,
10135         
10136         toolTip : false,
10137         
10138         intervalID : false,
10139         
10140         maskEl : false,
10141         
10142         apply : function()
10143         {
10144             if(this.isApplied){
10145                 return;
10146             }
10147             
10148             this.maskEl = {
10149                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10150                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10151                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10152                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10153             };
10154             
10155             this.maskEl.top.enableDisplayMode("block");
10156             this.maskEl.left.enableDisplayMode("block");
10157             this.maskEl.bottom.enableDisplayMode("block");
10158             this.maskEl.right.enableDisplayMode("block");
10159             
10160             this.toolTip = new Roo.bootstrap.Tooltip({
10161                 cls : 'roo-form-error-popover',
10162                 alignment : {
10163                     'left' : ['r-l', [-2,0], 'right'],
10164                     'right' : ['l-r', [2,0], 'left'],
10165                     'bottom' : ['tl-bl', [0,2], 'top'],
10166                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10167                 }
10168             });
10169             
10170             this.toolTip.render(Roo.get(document.body));
10171
10172             this.toolTip.el.enableDisplayMode("block");
10173             
10174             Roo.get(document.body).on('click', function(){
10175                 this.unmask();
10176             }, this);
10177             
10178             Roo.get(document.body).on('touchstart', function(){
10179                 this.unmask();
10180             }, this);
10181             
10182             this.isApplied = true
10183         },
10184         
10185         mask : function(form, target)
10186         {
10187             this.form = form;
10188             
10189             this.target = target;
10190             
10191             if(!this.form.errorMask || !target.el){
10192                 return;
10193             }
10194             
10195             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10196             
10197             Roo.log(scrollable);
10198             
10199             var ot = this.target.el.calcOffsetsTo(scrollable);
10200             
10201             var scrollTo = ot[1] - this.form.maskOffset;
10202             
10203             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10204             
10205             scrollable.scrollTo('top', scrollTo);
10206             
10207             var box = this.target.el.getBox();
10208             Roo.log(box);
10209             var zIndex = Roo.bootstrap.Modal.zIndex++;
10210
10211             
10212             this.maskEl.top.setStyle('position', 'absolute');
10213             this.maskEl.top.setStyle('z-index', zIndex);
10214             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10215             this.maskEl.top.setLeft(0);
10216             this.maskEl.top.setTop(0);
10217             this.maskEl.top.show();
10218             
10219             this.maskEl.left.setStyle('position', 'absolute');
10220             this.maskEl.left.setStyle('z-index', zIndex);
10221             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10222             this.maskEl.left.setLeft(0);
10223             this.maskEl.left.setTop(box.y - this.padding);
10224             this.maskEl.left.show();
10225
10226             this.maskEl.bottom.setStyle('position', 'absolute');
10227             this.maskEl.bottom.setStyle('z-index', zIndex);
10228             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10229             this.maskEl.bottom.setLeft(0);
10230             this.maskEl.bottom.setTop(box.bottom + this.padding);
10231             this.maskEl.bottom.show();
10232
10233             this.maskEl.right.setStyle('position', 'absolute');
10234             this.maskEl.right.setStyle('z-index', zIndex);
10235             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10236             this.maskEl.right.setLeft(box.right + this.padding);
10237             this.maskEl.right.setTop(box.y - this.padding);
10238             this.maskEl.right.show();
10239
10240             this.toolTip.bindEl = this.target.el;
10241
10242             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10243
10244             var tip = this.target.blankText;
10245
10246             if(this.target.getValue() !== '' ) {
10247                 
10248                 if (this.target.invalidText.length) {
10249                     tip = this.target.invalidText;
10250                 } else if (this.target.regexText.length){
10251                     tip = this.target.regexText;
10252                 }
10253             }
10254
10255             this.toolTip.show(tip);
10256
10257             this.intervalID = window.setInterval(function() {
10258                 Roo.bootstrap.Form.popover.unmask();
10259             }, 10000);
10260
10261             window.onwheel = function(){ return false;};
10262             
10263             (function(){ this.isMasked = true; }).defer(500, this);
10264             
10265         },
10266         
10267         unmask : function()
10268         {
10269             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10270                 return;
10271             }
10272             
10273             this.maskEl.top.setStyle('position', 'absolute');
10274             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10275             this.maskEl.top.hide();
10276
10277             this.maskEl.left.setStyle('position', 'absolute');
10278             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10279             this.maskEl.left.hide();
10280
10281             this.maskEl.bottom.setStyle('position', 'absolute');
10282             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10283             this.maskEl.bottom.hide();
10284
10285             this.maskEl.right.setStyle('position', 'absolute');
10286             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10287             this.maskEl.right.hide();
10288             
10289             this.toolTip.hide();
10290             
10291             this.toolTip.el.hide();
10292             
10293             window.onwheel = function(){ return true;};
10294             
10295             if(this.intervalID){
10296                 window.clearInterval(this.intervalID);
10297                 this.intervalID = false;
10298             }
10299             
10300             this.isMasked = false;
10301             
10302         }
10303         
10304     }
10305     
10306 });
10307
10308 /*
10309  * Based on:
10310  * Ext JS Library 1.1.1
10311  * Copyright(c) 2006-2007, Ext JS, LLC.
10312  *
10313  * Originally Released Under LGPL - original licence link has changed is not relivant.
10314  *
10315  * Fork - LGPL
10316  * <script type="text/javascript">
10317  */
10318 /**
10319  * @class Roo.form.VTypes
10320  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10321  * @singleton
10322  */
10323 Roo.form.VTypes = function(){
10324     // closure these in so they are only created once.
10325     var alpha = /^[a-zA-Z_]+$/;
10326     var alphanum = /^[a-zA-Z0-9_]+$/;
10327     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10328     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10329
10330     // All these messages and functions are configurable
10331     return {
10332         /**
10333          * The function used to validate email addresses
10334          * @param {String} value The email address
10335          */
10336         'email' : function(v){
10337             return email.test(v);
10338         },
10339         /**
10340          * The error text to display when the email validation function returns false
10341          * @type String
10342          */
10343         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10344         /**
10345          * The keystroke filter mask to be applied on email input
10346          * @type RegExp
10347          */
10348         'emailMask' : /[a-z0-9_\.\-@]/i,
10349
10350         /**
10351          * The function used to validate URLs
10352          * @param {String} value The URL
10353          */
10354         'url' : function(v){
10355             return url.test(v);
10356         },
10357         /**
10358          * The error text to display when the url validation function returns false
10359          * @type String
10360          */
10361         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10362         
10363         /**
10364          * The function used to validate alpha values
10365          * @param {String} value The value
10366          */
10367         'alpha' : function(v){
10368             return alpha.test(v);
10369         },
10370         /**
10371          * The error text to display when the alpha validation function returns false
10372          * @type String
10373          */
10374         'alphaText' : 'This field should only contain letters and _',
10375         /**
10376          * The keystroke filter mask to be applied on alpha input
10377          * @type RegExp
10378          */
10379         'alphaMask' : /[a-z_]/i,
10380
10381         /**
10382          * The function used to validate alphanumeric values
10383          * @param {String} value The value
10384          */
10385         'alphanum' : function(v){
10386             return alphanum.test(v);
10387         },
10388         /**
10389          * The error text to display when the alphanumeric validation function returns false
10390          * @type String
10391          */
10392         'alphanumText' : 'This field should only contain letters, numbers and _',
10393         /**
10394          * The keystroke filter mask to be applied on alphanumeric input
10395          * @type RegExp
10396          */
10397         'alphanumMask' : /[a-z0-9_]/i
10398     };
10399 }();/*
10400  * - LGPL
10401  *
10402  * Input
10403  * 
10404  */
10405
10406 /**
10407  * @class Roo.bootstrap.Input
10408  * @extends Roo.bootstrap.Component
10409  * Bootstrap Input class
10410  * @cfg {Boolean} disabled is it disabled
10411  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10412  * @cfg {String} name name of the input
10413  * @cfg {string} fieldLabel - the label associated
10414  * @cfg {string} placeholder - placeholder to put in text.
10415  * @cfg {string}  before - input group add on before
10416  * @cfg {string} after - input group add on after
10417  * @cfg {string} size - (lg|sm) or leave empty..
10418  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10419  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10420  * @cfg {Number} md colspan out of 12 for computer-sized screens
10421  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10422  * @cfg {string} value default value of the input
10423  * @cfg {Number} labelWidth set the width of label 
10424  * @cfg {Number} labellg set the width of label (1-12)
10425  * @cfg {Number} labelmd set the width of label (1-12)
10426  * @cfg {Number} labelsm set the width of label (1-12)
10427  * @cfg {Number} labelxs set the width of label (1-12)
10428  * @cfg {String} labelAlign (top|left)
10429  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10430  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10431  * @cfg {String} indicatorpos (left|right) default left
10432  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10433  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10434  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10435
10436  * @cfg {String} align (left|center|right) Default left
10437  * @cfg {Boolean} forceFeedback (true|false) Default false
10438  * 
10439  * @constructor
10440  * Create a new Input
10441  * @param {Object} config The config object
10442  */
10443
10444 Roo.bootstrap.Input = function(config){
10445     
10446     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10447     
10448     this.addEvents({
10449         /**
10450          * @event focus
10451          * Fires when this field receives input focus.
10452          * @param {Roo.form.Field} this
10453          */
10454         focus : true,
10455         /**
10456          * @event blur
10457          * Fires when this field loses input focus.
10458          * @param {Roo.form.Field} this
10459          */
10460         blur : true,
10461         /**
10462          * @event specialkey
10463          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10464          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10465          * @param {Roo.form.Field} this
10466          * @param {Roo.EventObject} e The event object
10467          */
10468         specialkey : true,
10469         /**
10470          * @event change
10471          * Fires just before the field blurs if the field value has changed.
10472          * @param {Roo.form.Field} this
10473          * @param {Mixed} newValue The new value
10474          * @param {Mixed} oldValue The original value
10475          */
10476         change : true,
10477         /**
10478          * @event invalid
10479          * Fires after the field has been marked as invalid.
10480          * @param {Roo.form.Field} this
10481          * @param {String} msg The validation message
10482          */
10483         invalid : true,
10484         /**
10485          * @event valid
10486          * Fires after the field has been validated with no errors.
10487          * @param {Roo.form.Field} this
10488          */
10489         valid : true,
10490          /**
10491          * @event keyup
10492          * Fires after the key up
10493          * @param {Roo.form.Field} this
10494          * @param {Roo.EventObject}  e The event Object
10495          */
10496         keyup : true
10497     });
10498 };
10499
10500 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10501      /**
10502      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10503       automatic validation (defaults to "keyup").
10504      */
10505     validationEvent : "keyup",
10506      /**
10507      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10508      */
10509     validateOnBlur : true,
10510     /**
10511      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10512      */
10513     validationDelay : 250,
10514      /**
10515      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10516      */
10517     focusClass : "x-form-focus",  // not needed???
10518     
10519        
10520     /**
10521      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     invalidClass : "has-warning",
10524     
10525     /**
10526      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10527      */
10528     validClass : "has-success",
10529     
10530     /**
10531      * @cfg {Boolean} hasFeedback (true|false) default true
10532      */
10533     hasFeedback : true,
10534     
10535     /**
10536      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     invalidFeedbackClass : "glyphicon-warning-sign",
10539     
10540     /**
10541      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10542      */
10543     validFeedbackClass : "glyphicon-ok",
10544     
10545     /**
10546      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10547      */
10548     selectOnFocus : false,
10549     
10550      /**
10551      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10552      */
10553     maskRe : null,
10554        /**
10555      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10556      */
10557     vtype : null,
10558     
10559       /**
10560      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10561      */
10562     disableKeyFilter : false,
10563     
10564        /**
10565      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10566      */
10567     disabled : false,
10568      /**
10569      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10570      */
10571     allowBlank : true,
10572     /**
10573      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10574      */
10575     blankText : "Please complete this mandatory field",
10576     
10577      /**
10578      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10579      */
10580     minLength : 0,
10581     /**
10582      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10583      */
10584     maxLength : Number.MAX_VALUE,
10585     /**
10586      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10587      */
10588     minLengthText : "The minimum length for this field is {0}",
10589     /**
10590      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10591      */
10592     maxLengthText : "The maximum length for this field is {0}",
10593   
10594     
10595     /**
10596      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10597      * If available, this function will be called only after the basic validators all return true, and will be passed the
10598      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10599      */
10600     validator : null,
10601     /**
10602      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10603      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10604      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10605      */
10606     regex : null,
10607     /**
10608      * @cfg {String} regexText -- Depricated - use Invalid Text
10609      */
10610     regexText : "",
10611     
10612     /**
10613      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10614      */
10615     invalidText : "",
10616     
10617     
10618     
10619     autocomplete: false,
10620     
10621     
10622     fieldLabel : '',
10623     inputType : 'text',
10624     
10625     name : false,
10626     placeholder: false,
10627     before : false,
10628     after : false,
10629     size : false,
10630     hasFocus : false,
10631     preventMark: false,
10632     isFormField : true,
10633     value : '',
10634     labelWidth : 2,
10635     labelAlign : false,
10636     readOnly : false,
10637     align : false,
10638     formatedValue : false,
10639     forceFeedback : false,
10640     
10641     indicatorpos : 'left',
10642     
10643     labellg : 0,
10644     labelmd : 0,
10645     labelsm : 0,
10646     labelxs : 0,
10647     
10648     capture : '',
10649     accept : '',
10650     
10651     parentLabelAlign : function()
10652     {
10653         var parent = this;
10654         while (parent.parent()) {
10655             parent = parent.parent();
10656             if (typeof(parent.labelAlign) !='undefined') {
10657                 return parent.labelAlign;
10658             }
10659         }
10660         return 'left';
10661         
10662     },
10663     
10664     getAutoCreate : function()
10665     {
10666         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10667         
10668         var id = Roo.id();
10669         
10670         var cfg = {};
10671         
10672         if(this.inputType != 'hidden'){
10673             cfg.cls = 'form-group' //input-group
10674         }
10675         
10676         var input =  {
10677             tag: 'input',
10678             id : id,
10679             type : this.inputType,
10680             value : this.value,
10681             cls : 'form-control',
10682             placeholder : this.placeholder || '',
10683             autocomplete : this.autocomplete || 'new-password'
10684         };
10685         if (this.inputType == 'file') {
10686             input.style = 'overflow:hidden'; // why not in CSS?
10687         }
10688         
10689         if(this.capture.length){
10690             input.capture = this.capture;
10691         }
10692         
10693         if(this.accept.length){
10694             input.accept = this.accept + "/*";
10695         }
10696         
10697         if(this.align){
10698             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10699         }
10700         
10701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10702             input.maxLength = this.maxLength;
10703         }
10704         
10705         if (this.disabled) {
10706             input.disabled=true;
10707         }
10708         
10709         if (this.readOnly) {
10710             input.readonly=true;
10711         }
10712         
10713         if (this.name) {
10714             input.name = this.name;
10715         }
10716         
10717         if (this.size) {
10718             input.cls += ' input-' + this.size;
10719         }
10720         
10721         var settings=this;
10722         ['xs','sm','md','lg'].map(function(size){
10723             if (settings[size]) {
10724                 cfg.cls += ' col-' + size + '-' + settings[size];
10725             }
10726         });
10727         
10728         var inputblock = input;
10729         
10730         var feedback = {
10731             tag: 'span',
10732             cls: 'glyphicon form-control-feedback'
10733         };
10734             
10735         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10736             
10737             inputblock = {
10738                 cls : 'has-feedback',
10739                 cn :  [
10740                     input,
10741                     feedback
10742                 ] 
10743             };  
10744         }
10745         
10746         if (this.before || this.after) {
10747             
10748             inputblock = {
10749                 cls : 'input-group',
10750                 cn :  [] 
10751             };
10752             
10753             if (this.before && typeof(this.before) == 'string') {
10754                 
10755                 inputblock.cn.push({
10756                     tag :'span',
10757                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10758                     html : this.before
10759                 });
10760             }
10761             if (this.before && typeof(this.before) == 'object') {
10762                 this.before = Roo.factory(this.before);
10763                 
10764                 inputblock.cn.push({
10765                     tag :'span',
10766                     cls : 'roo-input-before input-group-prepend   input-group-' +
10767                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10768                 });
10769             }
10770             
10771             inputblock.cn.push(input);
10772             
10773             if (this.after && typeof(this.after) == 'string') {
10774                 inputblock.cn.push({
10775                     tag :'span',
10776                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10777                     html : this.after
10778                 });
10779             }
10780             if (this.after && typeof(this.after) == 'object') {
10781                 this.after = Roo.factory(this.after);
10782                 
10783                 inputblock.cn.push({
10784                     tag :'span',
10785                     cls : 'roo-input-after input-group-append  input-group-' +
10786                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10787                 });
10788             }
10789             
10790             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10791                 inputblock.cls += ' has-feedback';
10792                 inputblock.cn.push(feedback);
10793             }
10794         };
10795         var indicator = {
10796             tag : 'i',
10797             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10798             tooltip : 'This field is required'
10799         };
10800         if (this.allowBlank ) {
10801             indicator.style = this.allowBlank ? ' display:none' : '';
10802         }
10803         if (align ==='left' && this.fieldLabel.length) {
10804             
10805             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10806             
10807             cfg.cn = [
10808                 indicator,
10809                 {
10810                     tag: 'label',
10811                     'for' :  id,
10812                     cls : 'control-label col-form-label',
10813                     html : this.fieldLabel
10814
10815                 },
10816                 {
10817                     cls : "", 
10818                     cn: [
10819                         inputblock
10820                     ]
10821                 }
10822             ];
10823             
10824             var labelCfg = cfg.cn[1];
10825             var contentCfg = cfg.cn[2];
10826             
10827             if(this.indicatorpos == 'right'){
10828                 cfg.cn = [
10829                     {
10830                         tag: 'label',
10831                         'for' :  id,
10832                         cls : 'control-label col-form-label',
10833                         cn : [
10834                             {
10835                                 tag : 'span',
10836                                 html : this.fieldLabel
10837                             },
10838                             indicator
10839                         ]
10840                     },
10841                     {
10842                         cls : "",
10843                         cn: [
10844                             inputblock
10845                         ]
10846                     }
10847
10848                 ];
10849                 
10850                 labelCfg = cfg.cn[0];
10851                 contentCfg = cfg.cn[1];
10852             
10853             }
10854             
10855             if(this.labelWidth > 12){
10856                 labelCfg.style = "width: " + this.labelWidth + 'px';
10857             }
10858             
10859             if(this.labelWidth < 13 && this.labelmd == 0){
10860                 this.labelmd = this.labelWidth;
10861             }
10862             
10863             if(this.labellg > 0){
10864                 labelCfg.cls += ' col-lg-' + this.labellg;
10865                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10866             }
10867             
10868             if(this.labelmd > 0){
10869                 labelCfg.cls += ' col-md-' + this.labelmd;
10870                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10871             }
10872             
10873             if(this.labelsm > 0){
10874                 labelCfg.cls += ' col-sm-' + this.labelsm;
10875                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10876             }
10877             
10878             if(this.labelxs > 0){
10879                 labelCfg.cls += ' col-xs-' + this.labelxs;
10880                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10881             }
10882             
10883             
10884         } else if ( this.fieldLabel.length) {
10885                 
10886             
10887             
10888             cfg.cn = [
10889                 {
10890                     tag : 'i',
10891                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10892                     tooltip : 'This field is required',
10893                     style : this.allowBlank ? ' display:none' : '' 
10894                 },
10895                 {
10896                     tag: 'label',
10897                    //cls : 'input-group-addon',
10898                     html : this.fieldLabel
10899
10900                 },
10901
10902                inputblock
10903
10904            ];
10905            
10906            if(this.indicatorpos == 'right'){
10907        
10908                 cfg.cn = [
10909                     {
10910                         tag: 'label',
10911                        //cls : 'input-group-addon',
10912                         html : this.fieldLabel
10913
10914                     },
10915                     {
10916                         tag : 'i',
10917                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10918                         tooltip : 'This field is required',
10919                         style : this.allowBlank ? ' display:none' : '' 
10920                     },
10921
10922                    inputblock
10923
10924                ];
10925
10926             }
10927
10928         } else {
10929             
10930             cfg.cn = [
10931
10932                     inputblock
10933
10934             ];
10935                 
10936                 
10937         };
10938         
10939         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10940            cfg.cls += ' navbar-form';
10941         }
10942         
10943         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10944             // on BS4 we do this only if not form 
10945             cfg.cls += ' navbar-form';
10946             cfg.tag = 'li';
10947         }
10948         
10949         return cfg;
10950         
10951     },
10952     /**
10953      * return the real input element.
10954      */
10955     inputEl: function ()
10956     {
10957         return this.el.select('input.form-control',true).first();
10958     },
10959     
10960     tooltipEl : function()
10961     {
10962         return this.inputEl();
10963     },
10964     
10965     indicatorEl : function()
10966     {
10967         if (Roo.bootstrap.version == 4) {
10968             return false; // not enabled in v4 yet.
10969         }
10970         
10971         var indicator = this.el.select('i.roo-required-indicator',true).first();
10972         
10973         if(!indicator){
10974             return false;
10975         }
10976         
10977         return indicator;
10978         
10979     },
10980     
10981     setDisabled : function(v)
10982     {
10983         var i  = this.inputEl().dom;
10984         if (!v) {
10985             i.removeAttribute('disabled');
10986             return;
10987             
10988         }
10989         i.setAttribute('disabled','true');
10990     },
10991     initEvents : function()
10992     {
10993           
10994         this.inputEl().on("keydown" , this.fireKey,  this);
10995         this.inputEl().on("focus", this.onFocus,  this);
10996         this.inputEl().on("blur", this.onBlur,  this);
10997         
10998         this.inputEl().relayEvent('keyup', this);
10999         
11000         this.indicator = this.indicatorEl();
11001         
11002         if(this.indicator){
11003             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11004         }
11005  
11006         // reference to original value for reset
11007         this.originalValue = this.getValue();
11008         //Roo.form.TextField.superclass.initEvents.call(this);
11009         if(this.validationEvent == 'keyup'){
11010             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11011             this.inputEl().on('keyup', this.filterValidation, this);
11012         }
11013         else if(this.validationEvent !== false){
11014             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11015         }
11016         
11017         if(this.selectOnFocus){
11018             this.on("focus", this.preFocus, this);
11019             
11020         }
11021         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11022             this.inputEl().on("keypress", this.filterKeys, this);
11023         } else {
11024             this.inputEl().relayEvent('keypress', this);
11025         }
11026        /* if(this.grow){
11027             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11028             this.el.on("click", this.autoSize,  this);
11029         }
11030         */
11031         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11032             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11033         }
11034         
11035         if (typeof(this.before) == 'object') {
11036             this.before.render(this.el.select('.roo-input-before',true).first());
11037         }
11038         if (typeof(this.after) == 'object') {
11039             this.after.render(this.el.select('.roo-input-after',true).first());
11040         }
11041         
11042         this.inputEl().on('change', this.onChange, this);
11043         
11044     },
11045     filterValidation : function(e){
11046         if(!e.isNavKeyPress()){
11047             this.validationTask.delay(this.validationDelay);
11048         }
11049     },
11050      /**
11051      * Validates the field value
11052      * @return {Boolean} True if the value is valid, else false
11053      */
11054     validate : function(){
11055         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11056         if(this.disabled || this.validateValue(this.getRawValue())){
11057             this.markValid();
11058             return true;
11059         }
11060         
11061         this.markInvalid();
11062         return false;
11063     },
11064     
11065     
11066     /**
11067      * Validates a value according to the field's validation rules and marks the field as invalid
11068      * if the validation fails
11069      * @param {Mixed} value The value to validate
11070      * @return {Boolean} True if the value is valid, else false
11071      */
11072     validateValue : function(value)
11073     {
11074         if(this.getVisibilityEl().hasClass('hidden')){
11075             return true;
11076         }
11077         
11078         if(value.length < 1)  { // if it's blank
11079             if(this.allowBlank){
11080                 return true;
11081             }
11082             return false;
11083         }
11084         
11085         if(value.length < this.minLength){
11086             return false;
11087         }
11088         if(value.length > this.maxLength){
11089             return false;
11090         }
11091         if(this.vtype){
11092             var vt = Roo.form.VTypes;
11093             if(!vt[this.vtype](value, this)){
11094                 return false;
11095             }
11096         }
11097         if(typeof this.validator == "function"){
11098             var msg = this.validator(value);
11099             if(msg !== true){
11100                 return false;
11101             }
11102             if (typeof(msg) == 'string') {
11103                 this.invalidText = msg;
11104             }
11105         }
11106         
11107         if(this.regex && !this.regex.test(value)){
11108             return false;
11109         }
11110         
11111         return true;
11112     },
11113     
11114      // private
11115     fireKey : function(e){
11116         //Roo.log('field ' + e.getKey());
11117         if(e.isNavKeyPress()){
11118             this.fireEvent("specialkey", this, e);
11119         }
11120     },
11121     focus : function (selectText){
11122         if(this.rendered){
11123             this.inputEl().focus();
11124             if(selectText === true){
11125                 this.inputEl().dom.select();
11126             }
11127         }
11128         return this;
11129     } ,
11130     
11131     onFocus : function(){
11132         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11133            // this.el.addClass(this.focusClass);
11134         }
11135         if(!this.hasFocus){
11136             this.hasFocus = true;
11137             this.startValue = this.getValue();
11138             this.fireEvent("focus", this);
11139         }
11140     },
11141     
11142     beforeBlur : Roo.emptyFn,
11143
11144     
11145     // private
11146     onBlur : function(){
11147         this.beforeBlur();
11148         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11149             //this.el.removeClass(this.focusClass);
11150         }
11151         this.hasFocus = false;
11152         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11153             this.validate();
11154         }
11155         var v = this.getValue();
11156         if(String(v) !== String(this.startValue)){
11157             this.fireEvent('change', this, v, this.startValue);
11158         }
11159         this.fireEvent("blur", this);
11160     },
11161     
11162     onChange : function(e)
11163     {
11164         var v = this.getValue();
11165         if(String(v) !== String(this.startValue)){
11166             this.fireEvent('change', this, v, this.startValue);
11167         }
11168         
11169     },
11170     
11171     /**
11172      * Resets the current field value to the originally loaded value and clears any validation messages
11173      */
11174     reset : function(){
11175         this.setValue(this.originalValue);
11176         this.validate();
11177     },
11178      /**
11179      * Returns the name of the field
11180      * @return {Mixed} name The name field
11181      */
11182     getName: function(){
11183         return this.name;
11184     },
11185      /**
11186      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11187      * @return {Mixed} value The field value
11188      */
11189     getValue : function(){
11190         
11191         var v = this.inputEl().getValue();
11192         
11193         return v;
11194     },
11195     /**
11196      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11197      * @return {Mixed} value The field value
11198      */
11199     getRawValue : function(){
11200         var v = this.inputEl().getValue();
11201         
11202         return v;
11203     },
11204     
11205     /**
11206      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11207      * @param {Mixed} value The value to set
11208      */
11209     setRawValue : function(v){
11210         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11211     },
11212     
11213     selectText : function(start, end){
11214         var v = this.getRawValue();
11215         if(v.length > 0){
11216             start = start === undefined ? 0 : start;
11217             end = end === undefined ? v.length : end;
11218             var d = this.inputEl().dom;
11219             if(d.setSelectionRange){
11220                 d.setSelectionRange(start, end);
11221             }else if(d.createTextRange){
11222                 var range = d.createTextRange();
11223                 range.moveStart("character", start);
11224                 range.moveEnd("character", v.length-end);
11225                 range.select();
11226             }
11227         }
11228     },
11229     
11230     /**
11231      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11232      * @param {Mixed} value The value to set
11233      */
11234     setValue : function(v){
11235         this.value = v;
11236         if(this.rendered){
11237             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11238             this.validate();
11239         }
11240     },
11241     
11242     /*
11243     processValue : function(value){
11244         if(this.stripCharsRe){
11245             var newValue = value.replace(this.stripCharsRe, '');
11246             if(newValue !== value){
11247                 this.setRawValue(newValue);
11248                 return newValue;
11249             }
11250         }
11251         return value;
11252     },
11253   */
11254     preFocus : function(){
11255         
11256         if(this.selectOnFocus){
11257             this.inputEl().dom.select();
11258         }
11259     },
11260     filterKeys : function(e){
11261         var k = e.getKey();
11262         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11263             return;
11264         }
11265         var c = e.getCharCode(), cc = String.fromCharCode(c);
11266         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11267             return;
11268         }
11269         if(!this.maskRe.test(cc)){
11270             e.stopEvent();
11271         }
11272     },
11273      /**
11274      * Clear any invalid styles/messages for this field
11275      */
11276     clearInvalid : function(){
11277         
11278         if(!this.el || this.preventMark){ // not rendered
11279             return;
11280         }
11281         
11282         
11283         this.el.removeClass([this.invalidClass, 'is-invalid']);
11284         
11285         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11286             
11287             var feedback = this.el.select('.form-control-feedback', true).first();
11288             
11289             if(feedback){
11290                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11291             }
11292             
11293         }
11294         
11295         if(this.indicator){
11296             this.indicator.removeClass('visible');
11297             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11298         }
11299         
11300         this.fireEvent('valid', this);
11301     },
11302     
11303      /**
11304      * Mark this field as valid
11305      */
11306     markValid : function()
11307     {
11308         if(!this.el  || this.preventMark){ // not rendered...
11309             return;
11310         }
11311         
11312         this.el.removeClass([this.invalidClass, this.validClass]);
11313         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11314
11315         var feedback = this.el.select('.form-control-feedback', true).first();
11316             
11317         if(feedback){
11318             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11319         }
11320         
11321         if(this.indicator){
11322             this.indicator.removeClass('visible');
11323             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11324         }
11325         
11326         if(this.disabled){
11327             return;
11328         }
11329         
11330            
11331         if(this.allowBlank && !this.getRawValue().length){
11332             return;
11333         }
11334         if (Roo.bootstrap.version == 3) {
11335             this.el.addClass(this.validClass);
11336         } else {
11337             this.inputEl().addClass('is-valid');
11338         }
11339
11340         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11341             
11342             var feedback = this.el.select('.form-control-feedback', true).first();
11343             
11344             if(feedback){
11345                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11346                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11347             }
11348             
11349         }
11350         
11351         this.fireEvent('valid', this);
11352     },
11353     
11354      /**
11355      * Mark this field as invalid
11356      * @param {String} msg The validation message
11357      */
11358     markInvalid : function(msg)
11359     {
11360         if(!this.el  || this.preventMark){ // not rendered
11361             return;
11362         }
11363         
11364         this.el.removeClass([this.invalidClass, this.validClass]);
11365         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11366         
11367         var feedback = this.el.select('.form-control-feedback', true).first();
11368             
11369         if(feedback){
11370             this.el.select('.form-control-feedback', true).first().removeClass(
11371                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11372         }
11373
11374         if(this.disabled){
11375             return;
11376         }
11377         
11378         if(this.allowBlank && !this.getRawValue().length){
11379             return;
11380         }
11381         
11382         if(this.indicator){
11383             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11384             this.indicator.addClass('visible');
11385         }
11386         if (Roo.bootstrap.version == 3) {
11387             this.el.addClass(this.invalidClass);
11388         } else {
11389             this.inputEl().addClass('is-invalid');
11390         }
11391         
11392         
11393         
11394         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11395             
11396             var feedback = this.el.select('.form-control-feedback', true).first();
11397             
11398             if(feedback){
11399                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11400                 
11401                 if(this.getValue().length || this.forceFeedback){
11402                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11403                 }
11404                 
11405             }
11406             
11407         }
11408         
11409         this.fireEvent('invalid', this, msg);
11410     },
11411     // private
11412     SafariOnKeyDown : function(event)
11413     {
11414         // this is a workaround for a password hang bug on chrome/ webkit.
11415         if (this.inputEl().dom.type != 'password') {
11416             return;
11417         }
11418         
11419         var isSelectAll = false;
11420         
11421         if(this.inputEl().dom.selectionEnd > 0){
11422             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11423         }
11424         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11425             event.preventDefault();
11426             this.setValue('');
11427             return;
11428         }
11429         
11430         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11431             
11432             event.preventDefault();
11433             // this is very hacky as keydown always get's upper case.
11434             //
11435             var cc = String.fromCharCode(event.getCharCode());
11436             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11437             
11438         }
11439     },
11440     adjustWidth : function(tag, w){
11441         tag = tag.toLowerCase();
11442         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11443             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11444                 if(tag == 'input'){
11445                     return w + 2;
11446                 }
11447                 if(tag == 'textarea'){
11448                     return w-2;
11449                 }
11450             }else if(Roo.isOpera){
11451                 if(tag == 'input'){
11452                     return w + 2;
11453                 }
11454                 if(tag == 'textarea'){
11455                     return w-2;
11456                 }
11457             }
11458         }
11459         return w;
11460     },
11461     
11462     setFieldLabel : function(v)
11463     {
11464         if(!this.rendered){
11465             return;
11466         }
11467         
11468         if(this.indicatorEl()){
11469             var ar = this.el.select('label > span',true);
11470             
11471             if (ar.elements.length) {
11472                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11473                 this.fieldLabel = v;
11474                 return;
11475             }
11476             
11477             var br = this.el.select('label',true);
11478             
11479             if(br.elements.length) {
11480                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11481                 this.fieldLabel = v;
11482                 return;
11483             }
11484             
11485             Roo.log('Cannot Found any of label > span || label in input');
11486             return;
11487         }
11488         
11489         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11490         this.fieldLabel = v;
11491         
11492         
11493     }
11494 });
11495
11496  
11497 /*
11498  * - LGPL
11499  *
11500  * Input
11501  * 
11502  */
11503
11504 /**
11505  * @class Roo.bootstrap.TextArea
11506  * @extends Roo.bootstrap.Input
11507  * Bootstrap TextArea class
11508  * @cfg {Number} cols Specifies the visible width of a text area
11509  * @cfg {Number} rows Specifies the visible number of lines in a text area
11510  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11511  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11512  * @cfg {string} html text
11513  * 
11514  * @constructor
11515  * Create a new TextArea
11516  * @param {Object} config The config object
11517  */
11518
11519 Roo.bootstrap.TextArea = function(config){
11520     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11521    
11522 };
11523
11524 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11525      
11526     cols : false,
11527     rows : 5,
11528     readOnly : false,
11529     warp : 'soft',
11530     resize : false,
11531     value: false,
11532     html: false,
11533     
11534     getAutoCreate : function(){
11535         
11536         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11537         
11538         var id = Roo.id();
11539         
11540         var cfg = {};
11541         
11542         if(this.inputType != 'hidden'){
11543             cfg.cls = 'form-group' //input-group
11544         }
11545         
11546         var input =  {
11547             tag: 'textarea',
11548             id : id,
11549             warp : this.warp,
11550             rows : this.rows,
11551             value : this.value || '',
11552             html: this.html || '',
11553             cls : 'form-control',
11554             placeholder : this.placeholder || '' 
11555             
11556         };
11557         
11558         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11559             input.maxLength = this.maxLength;
11560         }
11561         
11562         if(this.resize){
11563             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11564         }
11565         
11566         if(this.cols){
11567             input.cols = this.cols;
11568         }
11569         
11570         if (this.readOnly) {
11571             input.readonly = true;
11572         }
11573         
11574         if (this.name) {
11575             input.name = this.name;
11576         }
11577         
11578         if (this.size) {
11579             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11580         }
11581         
11582         var settings=this;
11583         ['xs','sm','md','lg'].map(function(size){
11584             if (settings[size]) {
11585                 cfg.cls += ' col-' + size + '-' + settings[size];
11586             }
11587         });
11588         
11589         var inputblock = input;
11590         
11591         if(this.hasFeedback && !this.allowBlank){
11592             
11593             var feedback = {
11594                 tag: 'span',
11595                 cls: 'glyphicon form-control-feedback'
11596             };
11597
11598             inputblock = {
11599                 cls : 'has-feedback',
11600                 cn :  [
11601                     input,
11602                     feedback
11603                 ] 
11604             };  
11605         }
11606         
11607         
11608         if (this.before || this.after) {
11609             
11610             inputblock = {
11611                 cls : 'input-group',
11612                 cn :  [] 
11613             };
11614             if (this.before) {
11615                 inputblock.cn.push({
11616                     tag :'span',
11617                     cls : 'input-group-addon',
11618                     html : this.before
11619                 });
11620             }
11621             
11622             inputblock.cn.push(input);
11623             
11624             if(this.hasFeedback && !this.allowBlank){
11625                 inputblock.cls += ' has-feedback';
11626                 inputblock.cn.push(feedback);
11627             }
11628             
11629             if (this.after) {
11630                 inputblock.cn.push({
11631                     tag :'span',
11632                     cls : 'input-group-addon',
11633                     html : this.after
11634                 });
11635             }
11636             
11637         }
11638         
11639         if (align ==='left' && this.fieldLabel.length) {
11640             cfg.cn = [
11641                 {
11642                     tag: 'label',
11643                     'for' :  id,
11644                     cls : 'control-label',
11645                     html : this.fieldLabel
11646                 },
11647                 {
11648                     cls : "",
11649                     cn: [
11650                         inputblock
11651                     ]
11652                 }
11653
11654             ];
11655             
11656             if(this.labelWidth > 12){
11657                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11658             }
11659
11660             if(this.labelWidth < 13 && this.labelmd == 0){
11661                 this.labelmd = this.labelWidth;
11662             }
11663
11664             if(this.labellg > 0){
11665                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11666                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11667             }
11668
11669             if(this.labelmd > 0){
11670                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11671                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11672             }
11673
11674             if(this.labelsm > 0){
11675                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11676                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11677             }
11678
11679             if(this.labelxs > 0){
11680                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11681                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11682             }
11683             
11684         } else if ( this.fieldLabel.length) {
11685             cfg.cn = [
11686
11687                {
11688                    tag: 'label',
11689                    //cls : 'input-group-addon',
11690                    html : this.fieldLabel
11691
11692                },
11693
11694                inputblock
11695
11696            ];
11697
11698         } else {
11699
11700             cfg.cn = [
11701
11702                 inputblock
11703
11704             ];
11705                 
11706         }
11707         
11708         if (this.disabled) {
11709             input.disabled=true;
11710         }
11711         
11712         return cfg;
11713         
11714     },
11715     /**
11716      * return the real textarea element.
11717      */
11718     inputEl: function ()
11719     {
11720         return this.el.select('textarea.form-control',true).first();
11721     },
11722     
11723     /**
11724      * Clear any invalid styles/messages for this field
11725      */
11726     clearInvalid : function()
11727     {
11728         
11729         if(!this.el || this.preventMark){ // not rendered
11730             return;
11731         }
11732         
11733         var label = this.el.select('label', true).first();
11734         var icon = this.el.select('i.fa-star', true).first();
11735         
11736         if(label && icon){
11737             icon.remove();
11738         }
11739         this.el.removeClass( this.validClass);
11740         this.inputEl().removeClass('is-invalid');
11741          
11742         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11743             
11744             var feedback = this.el.select('.form-control-feedback', true).first();
11745             
11746             if(feedback){
11747                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11748             }
11749             
11750         }
11751         
11752         this.fireEvent('valid', this);
11753     },
11754     
11755      /**
11756      * Mark this field as valid
11757      */
11758     markValid : function()
11759     {
11760         if(!this.el  || this.preventMark){ // not rendered
11761             return;
11762         }
11763         
11764         this.el.removeClass([this.invalidClass, this.validClass]);
11765         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11766         
11767         var feedback = this.el.select('.form-control-feedback', true).first();
11768             
11769         if(feedback){
11770             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11771         }
11772
11773         if(this.disabled || this.allowBlank){
11774             return;
11775         }
11776         
11777         var label = this.el.select('label', true).first();
11778         var icon = this.el.select('i.fa-star', true).first();
11779         
11780         if(label && icon){
11781             icon.remove();
11782         }
11783         if (Roo.bootstrap.version == 3) {
11784             this.el.addClass(this.validClass);
11785         } else {
11786             this.inputEl().addClass('is-valid');
11787         }
11788         
11789         
11790         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11791             
11792             var feedback = this.el.select('.form-control-feedback', true).first();
11793             
11794             if(feedback){
11795                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11796                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11797             }
11798             
11799         }
11800         
11801         this.fireEvent('valid', this);
11802     },
11803     
11804      /**
11805      * Mark this field as invalid
11806      * @param {String} msg The validation message
11807      */
11808     markInvalid : function(msg)
11809     {
11810         if(!this.el  || this.preventMark){ // not rendered
11811             return;
11812         }
11813         
11814         this.el.removeClass([this.invalidClass, this.validClass]);
11815         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11816         
11817         var feedback = this.el.select('.form-control-feedback', true).first();
11818             
11819         if(feedback){
11820             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11821         }
11822
11823         if(this.disabled || this.allowBlank){
11824             return;
11825         }
11826         
11827         var label = this.el.select('label', true).first();
11828         var icon = this.el.select('i.fa-star', true).first();
11829         
11830         if(!this.getValue().length && label && !icon){
11831             this.el.createChild({
11832                 tag : 'i',
11833                 cls : 'text-danger fa fa-lg fa-star',
11834                 tooltip : 'This field is required',
11835                 style : 'margin-right:5px;'
11836             }, label, true);
11837         }
11838         
11839         if (Roo.bootstrap.version == 3) {
11840             this.el.addClass(this.invalidClass);
11841         } else {
11842             this.inputEl().addClass('is-invalid');
11843         }
11844         
11845         // fixme ... this may be depricated need to test..
11846         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11847             
11848             var feedback = this.el.select('.form-control-feedback', true).first();
11849             
11850             if(feedback){
11851                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11852                 
11853                 if(this.getValue().length || this.forceFeedback){
11854                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11855                 }
11856                 
11857             }
11858             
11859         }
11860         
11861         this.fireEvent('invalid', this, msg);
11862     }
11863 });
11864
11865  
11866 /*
11867  * - LGPL
11868  *
11869  * trigger field - base class for combo..
11870  * 
11871  */
11872  
11873 /**
11874  * @class Roo.bootstrap.TriggerField
11875  * @extends Roo.bootstrap.Input
11876  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11877  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11878  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11879  * for which you can provide a custom implementation.  For example:
11880  * <pre><code>
11881 var trigger = new Roo.bootstrap.TriggerField();
11882 trigger.onTriggerClick = myTriggerFn;
11883 trigger.applyTo('my-field');
11884 </code></pre>
11885  *
11886  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11887  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11888  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11889  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11890  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11891
11892  * @constructor
11893  * Create a new TriggerField.
11894  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11895  * to the base TextField)
11896  */
11897 Roo.bootstrap.TriggerField = function(config){
11898     this.mimicing = false;
11899     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11900 };
11901
11902 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11903     /**
11904      * @cfg {String} triggerClass A CSS class to apply to the trigger
11905      */
11906      /**
11907      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11908      */
11909     hideTrigger:false,
11910
11911     /**
11912      * @cfg {Boolean} removable (true|false) special filter default false
11913      */
11914     removable : false,
11915     
11916     /** @cfg {Boolean} grow @hide */
11917     /** @cfg {Number} growMin @hide */
11918     /** @cfg {Number} growMax @hide */
11919
11920     /**
11921      * @hide 
11922      * @method
11923      */
11924     autoSize: Roo.emptyFn,
11925     // private
11926     monitorTab : true,
11927     // private
11928     deferHeight : true,
11929
11930     
11931     actionMode : 'wrap',
11932     
11933     caret : false,
11934     
11935     
11936     getAutoCreate : function(){
11937        
11938         var align = this.labelAlign || this.parentLabelAlign();
11939         
11940         var id = Roo.id();
11941         
11942         var cfg = {
11943             cls: 'form-group' //input-group
11944         };
11945         
11946         
11947         var input =  {
11948             tag: 'input',
11949             id : id,
11950             type : this.inputType,
11951             cls : 'form-control',
11952             autocomplete: 'new-password',
11953             placeholder : this.placeholder || '' 
11954             
11955         };
11956         if (this.name) {
11957             input.name = this.name;
11958         }
11959         if (this.size) {
11960             input.cls += ' input-' + this.size;
11961         }
11962         
11963         if (this.disabled) {
11964             input.disabled=true;
11965         }
11966         
11967         var inputblock = input;
11968         
11969         if(this.hasFeedback && !this.allowBlank){
11970             
11971             var feedback = {
11972                 tag: 'span',
11973                 cls: 'glyphicon form-control-feedback'
11974             };
11975             
11976             if(this.removable && !this.editable  ){
11977                 inputblock = {
11978                     cls : 'has-feedback',
11979                     cn :  [
11980                         inputblock,
11981                         {
11982                             tag: 'button',
11983                             html : 'x',
11984                             cls : 'roo-combo-removable-btn close'
11985                         },
11986                         feedback
11987                     ] 
11988                 };
11989             } else {
11990                 inputblock = {
11991                     cls : 'has-feedback',
11992                     cn :  [
11993                         inputblock,
11994                         feedback
11995                     ] 
11996                 };
11997             }
11998
11999         } else {
12000             if(this.removable && !this.editable ){
12001                 inputblock = {
12002                     cls : 'roo-removable',
12003                     cn :  [
12004                         inputblock,
12005                         {
12006                             tag: 'button',
12007                             html : 'x',
12008                             cls : 'roo-combo-removable-btn close'
12009                         }
12010                     ] 
12011                 };
12012             }
12013         }
12014         
12015         if (this.before || this.after) {
12016             
12017             inputblock = {
12018                 cls : 'input-group',
12019                 cn :  [] 
12020             };
12021             if (this.before) {
12022                 inputblock.cn.push({
12023                     tag :'span',
12024                     cls : 'input-group-addon input-group-prepend input-group-text',
12025                     html : this.before
12026                 });
12027             }
12028             
12029             inputblock.cn.push(input);
12030             
12031             if(this.hasFeedback && !this.allowBlank){
12032                 inputblock.cls += ' has-feedback';
12033                 inputblock.cn.push(feedback);
12034             }
12035             
12036             if (this.after) {
12037                 inputblock.cn.push({
12038                     tag :'span',
12039                     cls : 'input-group-addon input-group-append input-group-text',
12040                     html : this.after
12041                 });
12042             }
12043             
12044         };
12045         
12046       
12047         
12048         var ibwrap = inputblock;
12049         
12050         if(this.multiple){
12051             ibwrap = {
12052                 tag: 'ul',
12053                 cls: 'roo-select2-choices',
12054                 cn:[
12055                     {
12056                         tag: 'li',
12057                         cls: 'roo-select2-search-field',
12058                         cn: [
12059
12060                             inputblock
12061                         ]
12062                     }
12063                 ]
12064             };
12065                 
12066         }
12067         
12068         var combobox = {
12069             cls: 'roo-select2-container input-group',
12070             cn: [
12071                  {
12072                     tag: 'input',
12073                     type : 'hidden',
12074                     cls: 'form-hidden-field'
12075                 },
12076                 ibwrap
12077             ]
12078         };
12079         
12080         if(!this.multiple && this.showToggleBtn){
12081             
12082             var caret = {
12083                         tag: 'span',
12084                         cls: 'caret'
12085              };
12086             if (this.caret != false) {
12087                 caret = {
12088                      tag: 'i',
12089                      cls: 'fa fa-' + this.caret
12090                 };
12091                 
12092             }
12093             
12094             combobox.cn.push({
12095                 tag :'span',
12096                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12097                 cn : [
12098                     Roo.bootstrap.version == 3 ? caret : '',
12099                     {
12100                         tag: 'span',
12101                         cls: 'combobox-clear',
12102                         cn  : [
12103                             {
12104                                 tag : 'i',
12105                                 cls: 'icon-remove'
12106                             }
12107                         ]
12108                     }
12109                 ]
12110
12111             })
12112         }
12113         
12114         if(this.multiple){
12115             combobox.cls += ' roo-select2-container-multi';
12116         }
12117          var indicator = {
12118             tag : 'i',
12119             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12120             tooltip : 'This field is required'
12121         };
12122         if (Roo.bootstrap.version == 4) {
12123             indicator = {
12124                 tag : 'i',
12125                 style : 'display:none'
12126             };
12127         }
12128         
12129         
12130         if (align ==='left' && this.fieldLabel.length) {
12131             
12132             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12133
12134             cfg.cn = [
12135                 indicator,
12136                 {
12137                     tag: 'label',
12138                     'for' :  id,
12139                     cls : 'control-label',
12140                     html : this.fieldLabel
12141
12142                 },
12143                 {
12144                     cls : "", 
12145                     cn: [
12146                         combobox
12147                     ]
12148                 }
12149
12150             ];
12151             
12152             var labelCfg = cfg.cn[1];
12153             var contentCfg = cfg.cn[2];
12154             
12155             if(this.indicatorpos == 'right'){
12156                 cfg.cn = [
12157                     {
12158                         tag: 'label',
12159                         'for' :  id,
12160                         cls : 'control-label',
12161                         cn : [
12162                             {
12163                                 tag : 'span',
12164                                 html : this.fieldLabel
12165                             },
12166                             indicator
12167                         ]
12168                     },
12169                     {
12170                         cls : "", 
12171                         cn: [
12172                             combobox
12173                         ]
12174                     }
12175
12176                 ];
12177                 
12178                 labelCfg = cfg.cn[0];
12179                 contentCfg = cfg.cn[1];
12180             }
12181             
12182             if(this.labelWidth > 12){
12183                 labelCfg.style = "width: " + this.labelWidth + 'px';
12184             }
12185             
12186             if(this.labelWidth < 13 && this.labelmd == 0){
12187                 this.labelmd = this.labelWidth;
12188             }
12189             
12190             if(this.labellg > 0){
12191                 labelCfg.cls += ' col-lg-' + this.labellg;
12192                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12193             }
12194             
12195             if(this.labelmd > 0){
12196                 labelCfg.cls += ' col-md-' + this.labelmd;
12197                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12198             }
12199             
12200             if(this.labelsm > 0){
12201                 labelCfg.cls += ' col-sm-' + this.labelsm;
12202                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12203             }
12204             
12205             if(this.labelxs > 0){
12206                 labelCfg.cls += ' col-xs-' + this.labelxs;
12207                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12208             }
12209             
12210         } else if ( this.fieldLabel.length) {
12211 //                Roo.log(" label");
12212             cfg.cn = [
12213                 indicator,
12214                {
12215                    tag: 'label',
12216                    //cls : 'input-group-addon',
12217                    html : this.fieldLabel
12218
12219                },
12220
12221                combobox
12222
12223             ];
12224             
12225             if(this.indicatorpos == 'right'){
12226                 
12227                 cfg.cn = [
12228                     {
12229                        tag: 'label',
12230                        cn : [
12231                            {
12232                                tag : 'span',
12233                                html : this.fieldLabel
12234                            },
12235                            indicator
12236                        ]
12237
12238                     },
12239                     combobox
12240
12241                 ];
12242
12243             }
12244
12245         } else {
12246             
12247 //                Roo.log(" no label && no align");
12248                 cfg = combobox
12249                      
12250                 
12251         }
12252         
12253         var settings=this;
12254         ['xs','sm','md','lg'].map(function(size){
12255             if (settings[size]) {
12256                 cfg.cls += ' col-' + size + '-' + settings[size];
12257             }
12258         });
12259         
12260         return cfg;
12261         
12262     },
12263     
12264     
12265     
12266     // private
12267     onResize : function(w, h){
12268 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12269 //        if(typeof w == 'number'){
12270 //            var x = w - this.trigger.getWidth();
12271 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12272 //            this.trigger.setStyle('left', x+'px');
12273 //        }
12274     },
12275
12276     // private
12277     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12278
12279     // private
12280     getResizeEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     getPositionEl : function(){
12286         return this.inputEl();
12287     },
12288
12289     // private
12290     alignErrorIcon : function(){
12291         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12292     },
12293
12294     // private
12295     initEvents : function(){
12296         
12297         this.createList();
12298         
12299         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12300         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12301         if(!this.multiple && this.showToggleBtn){
12302             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12303             if(this.hideTrigger){
12304                 this.trigger.setDisplayed(false);
12305             }
12306             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12307         }
12308         
12309         if(this.multiple){
12310             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12311         }
12312         
12313         if(this.removable && !this.editable && !this.tickable){
12314             var close = this.closeTriggerEl();
12315             
12316             if(close){
12317                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12318                 close.on('click', this.removeBtnClick, this, close);
12319             }
12320         }
12321         
12322         //this.trigger.addClassOnOver('x-form-trigger-over');
12323         //this.trigger.addClassOnClick('x-form-trigger-click');
12324         
12325         //if(!this.width){
12326         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12327         //}
12328     },
12329     
12330     closeTriggerEl : function()
12331     {
12332         var close = this.el.select('.roo-combo-removable-btn', true).first();
12333         return close ? close : false;
12334     },
12335     
12336     removeBtnClick : function(e, h, el)
12337     {
12338         e.preventDefault();
12339         
12340         if(this.fireEvent("remove", this) !== false){
12341             this.reset();
12342             this.fireEvent("afterremove", this)
12343         }
12344     },
12345     
12346     createList : function()
12347     {
12348         this.list = Roo.get(document.body).createChild({
12349             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12350             cls: 'typeahead typeahead-long dropdown-menu shadow',
12351             style: 'display:none'
12352         });
12353         
12354         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12355         
12356     },
12357
12358     // private
12359     initTrigger : function(){
12360        
12361     },
12362
12363     // private
12364     onDestroy : function(){
12365         if(this.trigger){
12366             this.trigger.removeAllListeners();
12367           //  this.trigger.remove();
12368         }
12369         //if(this.wrap){
12370         //    this.wrap.remove();
12371         //}
12372         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12373     },
12374
12375     // private
12376     onFocus : function(){
12377         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12378         /*
12379         if(!this.mimicing){
12380             this.wrap.addClass('x-trigger-wrap-focus');
12381             this.mimicing = true;
12382             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12383             if(this.monitorTab){
12384                 this.el.on("keydown", this.checkTab, this);
12385             }
12386         }
12387         */
12388     },
12389
12390     // private
12391     checkTab : function(e){
12392         if(e.getKey() == e.TAB){
12393             this.triggerBlur();
12394         }
12395     },
12396
12397     // private
12398     onBlur : function(){
12399         // do nothing
12400     },
12401
12402     // private
12403     mimicBlur : function(e, t){
12404         /*
12405         if(!this.wrap.contains(t) && this.validateBlur()){
12406             this.triggerBlur();
12407         }
12408         */
12409     },
12410
12411     // private
12412     triggerBlur : function(){
12413         this.mimicing = false;
12414         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12415         if(this.monitorTab){
12416             this.el.un("keydown", this.checkTab, this);
12417         }
12418         //this.wrap.removeClass('x-trigger-wrap-focus');
12419         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12420     },
12421
12422     // private
12423     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12424     validateBlur : function(e, t){
12425         return true;
12426     },
12427
12428     // private
12429     onDisable : function(){
12430         this.inputEl().dom.disabled = true;
12431         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12432         //if(this.wrap){
12433         //    this.wrap.addClass('x-item-disabled');
12434         //}
12435     },
12436
12437     // private
12438     onEnable : function(){
12439         this.inputEl().dom.disabled = false;
12440         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12441         //if(this.wrap){
12442         //    this.el.removeClass('x-item-disabled');
12443         //}
12444     },
12445
12446     // private
12447     onShow : function(){
12448         var ae = this.getActionEl();
12449         
12450         if(ae){
12451             ae.dom.style.display = '';
12452             ae.dom.style.visibility = 'visible';
12453         }
12454     },
12455
12456     // private
12457     
12458     onHide : function(){
12459         var ae = this.getActionEl();
12460         ae.dom.style.display = 'none';
12461     },
12462
12463     /**
12464      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12465      * by an implementing function.
12466      * @method
12467      * @param {EventObject} e
12468      */
12469     onTriggerClick : Roo.emptyFn
12470 });
12471  
12472 /*
12473 * Licence: LGPL
12474 */
12475
12476 /**
12477  * @class Roo.bootstrap.CardUploader
12478  * @extends Roo.bootstrap.Button
12479  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12480  * @cfg {Number} errorTimeout default 3000
12481  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12482  * @cfg {Array}  html The button text.
12483
12484  *
12485  * @constructor
12486  * Create a new CardUploader
12487  * @param {Object} config The config object
12488  */
12489
12490 Roo.bootstrap.CardUploader = function(config){
12491     
12492  
12493     
12494     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12495     
12496     
12497     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12498         return r.data.id
12499         });
12500     
12501     
12502 };
12503
12504 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12505     
12506      
12507     errorTimeout : 3000,
12508      
12509     images : false,
12510    
12511     fileCollection : false,
12512     allowBlank : true,
12513     
12514     getAutoCreate : function()
12515     {
12516         
12517         var cfg =  {
12518             cls :'form-group' ,
12519             cn : [
12520                
12521                 {
12522                     tag: 'label',
12523                    //cls : 'input-group-addon',
12524                     html : this.fieldLabel
12525
12526                 },
12527
12528                 {
12529                     tag: 'input',
12530                     type : 'hidden',
12531                     value : this.value,
12532                     cls : 'd-none  form-control'
12533                 },
12534                 
12535                 {
12536                     tag: 'input',
12537                     multiple : 'multiple',
12538                     type : 'file',
12539                     cls : 'd-none  roo-card-upload-selector'
12540                 },
12541                 
12542                 {
12543                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12544                 },
12545                 {
12546                     cls : 'card-columns roo-card-uploader-container'
12547                 }
12548
12549             ]
12550         };
12551            
12552          
12553         return cfg;
12554     },
12555     
12556     getChildContainer : function() /// what children are added to.
12557     {
12558         return this.containerEl;
12559     },
12560    
12561     getButtonContainer : function() /// what children are added to.
12562     {
12563         return this.el.select(".roo-card-uploader-button-container").first();
12564     },
12565    
12566     initEvents : function()
12567     {
12568         
12569         Roo.bootstrap.Input.prototype.initEvents.call(this);
12570         
12571         var t = this;
12572         this.addxtype({
12573             xns: Roo.bootstrap,
12574
12575             xtype : 'Button',
12576             container_method : 'getButtonContainer' ,            
12577             html :  this.html, // fix changable?
12578             cls : 'w-100 ',
12579             listeners : {
12580                 'click' : function(btn, e) {
12581                     t.onClick(e);
12582                 }
12583             }
12584         });
12585         
12586         
12587         
12588         
12589         this.urlAPI = (window.createObjectURL && window) || 
12590                                 (window.URL && URL.revokeObjectURL && URL) || 
12591                                 (window.webkitURL && webkitURL);
12592                         
12593          
12594          
12595          
12596         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12597         
12598         this.selectorEl.on('change', this.onFileSelected, this);
12599         if (this.images) {
12600             var t = this;
12601             this.images.forEach(function(img) {
12602                 t.addCard(img)
12603             });
12604             this.images = false;
12605         }
12606         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12607          
12608        
12609     },
12610     
12611    
12612     onClick : function(e)
12613     {
12614         e.preventDefault();
12615          
12616         this.selectorEl.dom.click();
12617          
12618     },
12619     
12620     onFileSelected : function(e)
12621     {
12622         e.preventDefault();
12623         
12624         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12625             return;
12626         }
12627         
12628         Roo.each(this.selectorEl.dom.files, function(file){    
12629             this.addFile(file);
12630         }, this);
12631          
12632     },
12633     
12634       
12635     
12636       
12637     
12638     addFile : function(file)
12639     {
12640            
12641         if(typeof(file) === 'string'){
12642             throw "Add file by name?"; // should not happen
12643             return;
12644         }
12645         
12646         if(!file || !this.urlAPI){
12647             return;
12648         }
12649         
12650         // file;
12651         // file.type;
12652         
12653         var _this = this;
12654         
12655         
12656         var url = _this.urlAPI.createObjectURL( file);
12657            
12658         this.addCard({
12659             id : Roo.bootstrap.CardUploader.ID--,
12660             is_uploaded : false,
12661             src : url,
12662             title : file.name,
12663             mimetype : file.type,
12664             preview : false,
12665             is_deleted : 0
12666         })
12667         
12668     },
12669     
12670     addCard : function (data)
12671     {
12672         // hidden input element?
12673         // if the file is not an image...
12674         //then we need to use something other that and header_image
12675         var t = this;
12676         //   remove.....
12677         var footer = [
12678             {
12679                 xns : Roo.bootstrap,
12680                 xtype : 'CardFooter',
12681                 items: [
12682                     {
12683                         xns : Roo.bootstrap,
12684                         xtype : 'Element',
12685                         cls : 'd-flex',
12686                         items : [
12687                             
12688                             {
12689                                 xns : Roo.bootstrap,
12690                                 xtype : 'Button',
12691                                 html : String.format("<small>{0}</small>", data.title),
12692                                 cls : 'col-11 text-left',
12693                                 size: 'sm',
12694                                 weight: 'link',
12695                                 fa : 'download',
12696                                 listeners : {
12697                                     click : function() {
12698                                         this.downloadCard(data.id)
12699                                     }
12700                                 }
12701                             },
12702                           
12703                             {
12704                                 xns : Roo.bootstrap,
12705                                 xtype : 'Button',
12706                                 
12707                                 size : 'sm',
12708                                 weight: 'danger',
12709                                 cls : 'col-1',
12710                                 fa : 'times',
12711                                 listeners : {
12712                                     click : function() {
12713                                         t.removeCard(data.id)
12714                                     }
12715                                 }
12716                             }
12717                         ]
12718                     }
12719                     
12720                 ] 
12721             }
12722             
12723         ];
12724
12725         var cn = this.addxtype(
12726             {
12727                  
12728                 xns : Roo.bootstrap,
12729                 xtype : 'Card',
12730                 closeable : true,
12731                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12732                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12733                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12734                 data : data,
12735                 html : false,
12736                  
12737                 items : footer,
12738                 initEvents : function() {
12739                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12740                     this.imgEl = this.el.select('.card-img-top').first();
12741                     if (this.imgEl) {
12742                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12743                         this.imgEl.set({ 'pointer' : 'cursor' });
12744                                   
12745                     }
12746                     
12747                   
12748                 }
12749                 
12750             }
12751         );
12752         // dont' really need ot update items.
12753         // this.items.push(cn);
12754         this.fileCollection.add(cn);
12755         this.updateInput();
12756         
12757     },
12758     removeCard : function(id)
12759     {
12760         
12761         var card  = this.fileCollection.get(id);
12762         card.data.is_deleted = 1;
12763         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12764         this.fileCollection.remove(card);
12765         //this.items = this.items.filter(function(e) { return e != card });
12766         // dont' really need ot update items.
12767         card.el.dom.parentNode.removeChild(card.el.dom);
12768         
12769     },
12770     reset: function()
12771     {
12772         this.fileCollection.each(function(card) {
12773             card.el.dom.parentNode.removeChild(card.el.dom);    
12774         });
12775         this.fileCollection.clear();
12776         this.updateInput();
12777     },
12778     
12779     updateInput : function()
12780     {
12781         var data = [];
12782         this.fileCollection.each(function(e) {
12783             data.push(e.data);
12784         });
12785         
12786         this.inputEl().dom.value = JSON.stringify(data);
12787     }
12788     
12789     
12790 });
12791
12792
12793 Roo.bootstrap.CardUploader.ID = -1;/*
12794  * Based on:
12795  * Ext JS Library 1.1.1
12796  * Copyright(c) 2006-2007, Ext JS, LLC.
12797  *
12798  * Originally Released Under LGPL - original licence link has changed is not relivant.
12799  *
12800  * Fork - LGPL
12801  * <script type="text/javascript">
12802  */
12803
12804
12805 /**
12806  * @class Roo.data.SortTypes
12807  * @singleton
12808  * Defines the default sorting (casting?) comparison functions used when sorting data.
12809  */
12810 Roo.data.SortTypes = {
12811     /**
12812      * Default sort that does nothing
12813      * @param {Mixed} s The value being converted
12814      * @return {Mixed} The comparison value
12815      */
12816     none : function(s){
12817         return s;
12818     },
12819     
12820     /**
12821      * The regular expression used to strip tags
12822      * @type {RegExp}
12823      * @property
12824      */
12825     stripTagsRE : /<\/?[^>]+>/gi,
12826     
12827     /**
12828      * Strips all HTML tags to sort on text only
12829      * @param {Mixed} s The value being converted
12830      * @return {String} The comparison value
12831      */
12832     asText : function(s){
12833         return String(s).replace(this.stripTagsRE, "");
12834     },
12835     
12836     /**
12837      * Strips all HTML tags to sort on text only - Case insensitive
12838      * @param {Mixed} s The value being converted
12839      * @return {String} The comparison value
12840      */
12841     asUCText : function(s){
12842         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12843     },
12844     
12845     /**
12846      * Case insensitive string
12847      * @param {Mixed} s The value being converted
12848      * @return {String} The comparison value
12849      */
12850     asUCString : function(s) {
12851         return String(s).toUpperCase();
12852     },
12853     
12854     /**
12855      * Date sorting
12856      * @param {Mixed} s The value being converted
12857      * @return {Number} The comparison value
12858      */
12859     asDate : function(s) {
12860         if(!s){
12861             return 0;
12862         }
12863         if(s instanceof Date){
12864             return s.getTime();
12865         }
12866         return Date.parse(String(s));
12867     },
12868     
12869     /**
12870      * Float sorting
12871      * @param {Mixed} s The value being converted
12872      * @return {Float} The comparison value
12873      */
12874     asFloat : function(s) {
12875         var val = parseFloat(String(s).replace(/,/g, ""));
12876         if(isNaN(val)) {
12877             val = 0;
12878         }
12879         return val;
12880     },
12881     
12882     /**
12883      * Integer sorting
12884      * @param {Mixed} s The value being converted
12885      * @return {Number} The comparison value
12886      */
12887     asInt : function(s) {
12888         var val = parseInt(String(s).replace(/,/g, ""));
12889         if(isNaN(val)) {
12890             val = 0;
12891         }
12892         return val;
12893     }
12894 };/*
12895  * Based on:
12896  * Ext JS Library 1.1.1
12897  * Copyright(c) 2006-2007, Ext JS, LLC.
12898  *
12899  * Originally Released Under LGPL - original licence link has changed is not relivant.
12900  *
12901  * Fork - LGPL
12902  * <script type="text/javascript">
12903  */
12904
12905 /**
12906 * @class Roo.data.Record
12907  * Instances of this class encapsulate both record <em>definition</em> information, and record
12908  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12909  * to access Records cached in an {@link Roo.data.Store} object.<br>
12910  * <p>
12911  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12912  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12913  * objects.<br>
12914  * <p>
12915  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12916  * @constructor
12917  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12918  * {@link #create}. The parameters are the same.
12919  * @param {Array} data An associative Array of data values keyed by the field name.
12920  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12921  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12922  * not specified an integer id is generated.
12923  */
12924 Roo.data.Record = function(data, id){
12925     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12926     this.data = data;
12927 };
12928
12929 /**
12930  * Generate a constructor for a specific record layout.
12931  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12932  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12933  * Each field definition object may contain the following properties: <ul>
12934  * <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,
12935  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12936  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12937  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12938  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12939  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12940  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12941  * this may be omitted.</p></li>
12942  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12943  * <ul><li>auto (Default, implies no conversion)</li>
12944  * <li>string</li>
12945  * <li>int</li>
12946  * <li>float</li>
12947  * <li>boolean</li>
12948  * <li>date</li></ul></p></li>
12949  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12950  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12951  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12952  * by the Reader into an object that will be stored in the Record. It is passed the
12953  * following parameters:<ul>
12954  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12955  * </ul></p></li>
12956  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12957  * </ul>
12958  * <br>usage:<br><pre><code>
12959 var TopicRecord = Roo.data.Record.create(
12960     {name: 'title', mapping: 'topic_title'},
12961     {name: 'author', mapping: 'username'},
12962     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12963     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12964     {name: 'lastPoster', mapping: 'user2'},
12965     {name: 'excerpt', mapping: 'post_text'}
12966 );
12967
12968 var myNewRecord = new TopicRecord({
12969     title: 'Do my job please',
12970     author: 'noobie',
12971     totalPosts: 1,
12972     lastPost: new Date(),
12973     lastPoster: 'Animal',
12974     excerpt: 'No way dude!'
12975 });
12976 myStore.add(myNewRecord);
12977 </code></pre>
12978  * @method create
12979  * @static
12980  */
12981 Roo.data.Record.create = function(o){
12982     var f = function(){
12983         f.superclass.constructor.apply(this, arguments);
12984     };
12985     Roo.extend(f, Roo.data.Record);
12986     var p = f.prototype;
12987     p.fields = new Roo.util.MixedCollection(false, function(field){
12988         return field.name;
12989     });
12990     for(var i = 0, len = o.length; i < len; i++){
12991         p.fields.add(new Roo.data.Field(o[i]));
12992     }
12993     f.getField = function(name){
12994         return p.fields.get(name);  
12995     };
12996     return f;
12997 };
12998
12999 Roo.data.Record.AUTO_ID = 1000;
13000 Roo.data.Record.EDIT = 'edit';
13001 Roo.data.Record.REJECT = 'reject';
13002 Roo.data.Record.COMMIT = 'commit';
13003
13004 Roo.data.Record.prototype = {
13005     /**
13006      * Readonly flag - true if this record has been modified.
13007      * @type Boolean
13008      */
13009     dirty : false,
13010     editing : false,
13011     error: null,
13012     modified: null,
13013
13014     // private
13015     join : function(store){
13016         this.store = store;
13017     },
13018
13019     /**
13020      * Set the named field to the specified value.
13021      * @param {String} name The name of the field to set.
13022      * @param {Object} value The value to set the field to.
13023      */
13024     set : function(name, value){
13025         if(this.data[name] == value){
13026             return;
13027         }
13028         this.dirty = true;
13029         if(!this.modified){
13030             this.modified = {};
13031         }
13032         if(typeof this.modified[name] == 'undefined'){
13033             this.modified[name] = this.data[name];
13034         }
13035         this.data[name] = value;
13036         if(!this.editing && this.store){
13037             this.store.afterEdit(this);
13038         }       
13039     },
13040
13041     /**
13042      * Get the value of the named field.
13043      * @param {String} name The name of the field to get the value of.
13044      * @return {Object} The value of the field.
13045      */
13046     get : function(name){
13047         return this.data[name]; 
13048     },
13049
13050     // private
13051     beginEdit : function(){
13052         this.editing = true;
13053         this.modified = {}; 
13054     },
13055
13056     // private
13057     cancelEdit : function(){
13058         this.editing = false;
13059         delete this.modified;
13060     },
13061
13062     // private
13063     endEdit : function(){
13064         this.editing = false;
13065         if(this.dirty && this.store){
13066             this.store.afterEdit(this);
13067         }
13068     },
13069
13070     /**
13071      * Usually called by the {@link Roo.data.Store} which owns the Record.
13072      * Rejects all changes made to the Record since either creation, or the last commit operation.
13073      * Modified fields are reverted to their original values.
13074      * <p>
13075      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13076      * of reject operations.
13077      */
13078     reject : function(){
13079         var m = this.modified;
13080         for(var n in m){
13081             if(typeof m[n] != "function"){
13082                 this.data[n] = m[n];
13083             }
13084         }
13085         this.dirty = false;
13086         delete this.modified;
13087         this.editing = false;
13088         if(this.store){
13089             this.store.afterReject(this);
13090         }
13091     },
13092
13093     /**
13094      * Usually called by the {@link Roo.data.Store} which owns the Record.
13095      * Commits all changes made to the Record since either creation, or the last commit operation.
13096      * <p>
13097      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13098      * of commit operations.
13099      */
13100     commit : function(){
13101         this.dirty = false;
13102         delete this.modified;
13103         this.editing = false;
13104         if(this.store){
13105             this.store.afterCommit(this);
13106         }
13107     },
13108
13109     // private
13110     hasError : function(){
13111         return this.error != null;
13112     },
13113
13114     // private
13115     clearError : function(){
13116         this.error = null;
13117     },
13118
13119     /**
13120      * Creates a copy of this record.
13121      * @param {String} id (optional) A new record id if you don't want to use this record's id
13122      * @return {Record}
13123      */
13124     copy : function(newId) {
13125         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13126     }
13127 };/*
13128  * Based on:
13129  * Ext JS Library 1.1.1
13130  * Copyright(c) 2006-2007, Ext JS, LLC.
13131  *
13132  * Originally Released Under LGPL - original licence link has changed is not relivant.
13133  *
13134  * Fork - LGPL
13135  * <script type="text/javascript">
13136  */
13137
13138
13139
13140 /**
13141  * @class Roo.data.Store
13142  * @extends Roo.util.Observable
13143  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13144  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13145  * <p>
13146  * 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
13147  * has no knowledge of the format of the data returned by the Proxy.<br>
13148  * <p>
13149  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13150  * instances from the data object. These records are cached and made available through accessor functions.
13151  * @constructor
13152  * Creates a new Store.
13153  * @param {Object} config A config object containing the objects needed for the Store to access data,
13154  * and read the data into Records.
13155  */
13156 Roo.data.Store = function(config){
13157     this.data = new Roo.util.MixedCollection(false);
13158     this.data.getKey = function(o){
13159         return o.id;
13160     };
13161     this.baseParams = {};
13162     // private
13163     this.paramNames = {
13164         "start" : "start",
13165         "limit" : "limit",
13166         "sort" : "sort",
13167         "dir" : "dir",
13168         "multisort" : "_multisort"
13169     };
13170
13171     if(config && config.data){
13172         this.inlineData = config.data;
13173         delete config.data;
13174     }
13175
13176     Roo.apply(this, config);
13177     
13178     if(this.reader){ // reader passed
13179         this.reader = Roo.factory(this.reader, Roo.data);
13180         this.reader.xmodule = this.xmodule || false;
13181         if(!this.recordType){
13182             this.recordType = this.reader.recordType;
13183         }
13184         if(this.reader.onMetaChange){
13185             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13186         }
13187     }
13188
13189     if(this.recordType){
13190         this.fields = this.recordType.prototype.fields;
13191     }
13192     this.modified = [];
13193
13194     this.addEvents({
13195         /**
13196          * @event datachanged
13197          * Fires when the data cache has changed, and a widget which is using this Store
13198          * as a Record cache should refresh its view.
13199          * @param {Store} this
13200          */
13201         datachanged : true,
13202         /**
13203          * @event metachange
13204          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13205          * @param {Store} this
13206          * @param {Object} meta The JSON metadata
13207          */
13208         metachange : true,
13209         /**
13210          * @event add
13211          * Fires when Records have been added to the Store
13212          * @param {Store} this
13213          * @param {Roo.data.Record[]} records The array of Records added
13214          * @param {Number} index The index at which the record(s) were added
13215          */
13216         add : true,
13217         /**
13218          * @event remove
13219          * Fires when a Record has been removed from the Store
13220          * @param {Store} this
13221          * @param {Roo.data.Record} record The Record that was removed
13222          * @param {Number} index The index at which the record was removed
13223          */
13224         remove : true,
13225         /**
13226          * @event update
13227          * Fires when a Record has been updated
13228          * @param {Store} this
13229          * @param {Roo.data.Record} record The Record that was updated
13230          * @param {String} operation The update operation being performed.  Value may be one of:
13231          * <pre><code>
13232  Roo.data.Record.EDIT
13233  Roo.data.Record.REJECT
13234  Roo.data.Record.COMMIT
13235          * </code></pre>
13236          */
13237         update : true,
13238         /**
13239          * @event clear
13240          * Fires when the data cache has been cleared.
13241          * @param {Store} this
13242          */
13243         clear : true,
13244         /**
13245          * @event beforeload
13246          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13247          * the load action will be canceled.
13248          * @param {Store} this
13249          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13250          */
13251         beforeload : true,
13252         /**
13253          * @event beforeloadadd
13254          * Fires after a new set of Records has been loaded.
13255          * @param {Store} this
13256          * @param {Roo.data.Record[]} records The Records that were loaded
13257          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13258          */
13259         beforeloadadd : true,
13260         /**
13261          * @event load
13262          * Fires after a new set of Records has been loaded, before they are added to the store.
13263          * @param {Store} this
13264          * @param {Roo.data.Record[]} records The Records that were loaded
13265          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13266          * @params {Object} return from reader
13267          */
13268         load : true,
13269         /**
13270          * @event loadexception
13271          * Fires if an exception occurs in the Proxy during loading.
13272          * Called with the signature of the Proxy's "loadexception" event.
13273          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13274          * 
13275          * @param {Proxy} 
13276          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13277          * @param {Object} load options 
13278          * @param {Object} jsonData from your request (normally this contains the Exception)
13279          */
13280         loadexception : true
13281     });
13282     
13283     if(this.proxy){
13284         this.proxy = Roo.factory(this.proxy, Roo.data);
13285         this.proxy.xmodule = this.xmodule || false;
13286         this.relayEvents(this.proxy,  ["loadexception"]);
13287     }
13288     this.sortToggle = {};
13289     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13290
13291     Roo.data.Store.superclass.constructor.call(this);
13292
13293     if(this.inlineData){
13294         this.loadData(this.inlineData);
13295         delete this.inlineData;
13296     }
13297 };
13298
13299 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13300      /**
13301     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13302     * without a remote query - used by combo/forms at present.
13303     */
13304     
13305     /**
13306     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13307     */
13308     /**
13309     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13310     */
13311     /**
13312     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13313     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13314     */
13315     /**
13316     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13317     * on any HTTP request
13318     */
13319     /**
13320     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13321     */
13322     /**
13323     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13324     */
13325     multiSort: false,
13326     /**
13327     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13328     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13329     */
13330     remoteSort : false,
13331
13332     /**
13333     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13334      * loaded or when a record is removed. (defaults to false).
13335     */
13336     pruneModifiedRecords : false,
13337
13338     // private
13339     lastOptions : null,
13340
13341     /**
13342      * Add Records to the Store and fires the add event.
13343      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13344      */
13345     add : function(records){
13346         records = [].concat(records);
13347         for(var i = 0, len = records.length; i < len; i++){
13348             records[i].join(this);
13349         }
13350         var index = this.data.length;
13351         this.data.addAll(records);
13352         this.fireEvent("add", this, records, index);
13353     },
13354
13355     /**
13356      * Remove a Record from the Store and fires the remove event.
13357      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13358      */
13359     remove : function(record){
13360         var index = this.data.indexOf(record);
13361         this.data.removeAt(index);
13362  
13363         if(this.pruneModifiedRecords){
13364             this.modified.remove(record);
13365         }
13366         this.fireEvent("remove", this, record, index);
13367     },
13368
13369     /**
13370      * Remove all Records from the Store and fires the clear event.
13371      */
13372     removeAll : function(){
13373         this.data.clear();
13374         if(this.pruneModifiedRecords){
13375             this.modified = [];
13376         }
13377         this.fireEvent("clear", this);
13378     },
13379
13380     /**
13381      * Inserts Records to the Store at the given index and fires the add event.
13382      * @param {Number} index The start index at which to insert the passed Records.
13383      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13384      */
13385     insert : function(index, records){
13386         records = [].concat(records);
13387         for(var i = 0, len = records.length; i < len; i++){
13388             this.data.insert(index, records[i]);
13389             records[i].join(this);
13390         }
13391         this.fireEvent("add", this, records, index);
13392     },
13393
13394     /**
13395      * Get the index within the cache of the passed Record.
13396      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13397      * @return {Number} The index of the passed Record. Returns -1 if not found.
13398      */
13399     indexOf : function(record){
13400         return this.data.indexOf(record);
13401     },
13402
13403     /**
13404      * Get the index within the cache of the Record with the passed id.
13405      * @param {String} id The id of the Record to find.
13406      * @return {Number} The index of the Record. Returns -1 if not found.
13407      */
13408     indexOfId : function(id){
13409         return this.data.indexOfKey(id);
13410     },
13411
13412     /**
13413      * Get the Record with the specified id.
13414      * @param {String} id The id of the Record to find.
13415      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13416      */
13417     getById : function(id){
13418         return this.data.key(id);
13419     },
13420
13421     /**
13422      * Get the Record at the specified index.
13423      * @param {Number} index The index of the Record to find.
13424      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13425      */
13426     getAt : function(index){
13427         return this.data.itemAt(index);
13428     },
13429
13430     /**
13431      * Returns a range of Records between specified indices.
13432      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13433      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13434      * @return {Roo.data.Record[]} An array of Records
13435      */
13436     getRange : function(start, end){
13437         return this.data.getRange(start, end);
13438     },
13439
13440     // private
13441     storeOptions : function(o){
13442         o = Roo.apply({}, o);
13443         delete o.callback;
13444         delete o.scope;
13445         this.lastOptions = o;
13446     },
13447
13448     /**
13449      * Loads the Record cache from the configured Proxy using the configured Reader.
13450      * <p>
13451      * If using remote paging, then the first load call must specify the <em>start</em>
13452      * and <em>limit</em> properties in the options.params property to establish the initial
13453      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13454      * <p>
13455      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13456      * and this call will return before the new data has been loaded. Perform any post-processing
13457      * in a callback function, or in a "load" event handler.</strong>
13458      * <p>
13459      * @param {Object} options An object containing properties which control loading options:<ul>
13460      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13461      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13462      * passed the following arguments:<ul>
13463      * <li>r : Roo.data.Record[]</li>
13464      * <li>options: Options object from the load call</li>
13465      * <li>success: Boolean success indicator</li></ul></li>
13466      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13467      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13468      * </ul>
13469      */
13470     load : function(options){
13471         options = options || {};
13472         if(this.fireEvent("beforeload", this, options) !== false){
13473             this.storeOptions(options);
13474             var p = Roo.apply(options.params || {}, this.baseParams);
13475             // if meta was not loaded from remote source.. try requesting it.
13476             if (!this.reader.metaFromRemote) {
13477                 p._requestMeta = 1;
13478             }
13479             if(this.sortInfo && this.remoteSort){
13480                 var pn = this.paramNames;
13481                 p[pn["sort"]] = this.sortInfo.field;
13482                 p[pn["dir"]] = this.sortInfo.direction;
13483             }
13484             if (this.multiSort) {
13485                 var pn = this.paramNames;
13486                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13487             }
13488             
13489             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13490         }
13491     },
13492
13493     /**
13494      * Reloads the Record cache from the configured Proxy using the configured Reader and
13495      * the options from the last load operation performed.
13496      * @param {Object} options (optional) An object containing properties which may override the options
13497      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13498      * the most recently used options are reused).
13499      */
13500     reload : function(options){
13501         this.load(Roo.applyIf(options||{}, this.lastOptions));
13502     },
13503
13504     // private
13505     // Called as a callback by the Reader during a load operation.
13506     loadRecords : function(o, options, success){
13507         if(!o || success === false){
13508             if(success !== false){
13509                 this.fireEvent("load", this, [], options, o);
13510             }
13511             if(options.callback){
13512                 options.callback.call(options.scope || this, [], options, false);
13513             }
13514             return;
13515         }
13516         // if data returned failure - throw an exception.
13517         if (o.success === false) {
13518             // show a message if no listener is registered.
13519             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13520                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13521             }
13522             // loadmask wil be hooked into this..
13523             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13524             return;
13525         }
13526         var r = o.records, t = o.totalRecords || r.length;
13527         
13528         this.fireEvent("beforeloadadd", this, r, options, o);
13529         
13530         if(!options || options.add !== true){
13531             if(this.pruneModifiedRecords){
13532                 this.modified = [];
13533             }
13534             for(var i = 0, len = r.length; i < len; i++){
13535                 r[i].join(this);
13536             }
13537             if(this.snapshot){
13538                 this.data = this.snapshot;
13539                 delete this.snapshot;
13540             }
13541             this.data.clear();
13542             this.data.addAll(r);
13543             this.totalLength = t;
13544             this.applySort();
13545             this.fireEvent("datachanged", this);
13546         }else{
13547             this.totalLength = Math.max(t, this.data.length+r.length);
13548             this.add(r);
13549         }
13550         
13551         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13552                 
13553             var e = new Roo.data.Record({});
13554
13555             e.set(this.parent.displayField, this.parent.emptyTitle);
13556             e.set(this.parent.valueField, '');
13557
13558             this.insert(0, e);
13559         }
13560             
13561         this.fireEvent("load", this, r, options, o);
13562         if(options.callback){
13563             options.callback.call(options.scope || this, r, options, true);
13564         }
13565     },
13566
13567
13568     /**
13569      * Loads data from a passed data block. A Reader which understands the format of the data
13570      * must have been configured in the constructor.
13571      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13572      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13573      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13574      */
13575     loadData : function(o, append){
13576         var r = this.reader.readRecords(o);
13577         this.loadRecords(r, {add: append}, true);
13578     },
13579     
13580      /**
13581      * using 'cn' the nested child reader read the child array into it's child stores.
13582      * @param {Object} rec The record with a 'children array
13583      */
13584     loadDataFromChildren : function(rec)
13585     {
13586         this.loadData(this.reader.toLoadData(rec));
13587     },
13588     
13589
13590     /**
13591      * Gets the number of cached records.
13592      * <p>
13593      * <em>If using paging, this may not be the total size of the dataset. If the data object
13594      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13595      * the data set size</em>
13596      */
13597     getCount : function(){
13598         return this.data.length || 0;
13599     },
13600
13601     /**
13602      * Gets the total number of records in the dataset as returned by the server.
13603      * <p>
13604      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13605      * the dataset size</em>
13606      */
13607     getTotalCount : function(){
13608         return this.totalLength || 0;
13609     },
13610
13611     /**
13612      * Returns the sort state of the Store as an object with two properties:
13613      * <pre><code>
13614  field {String} The name of the field by which the Records are sorted
13615  direction {String} The sort order, "ASC" or "DESC"
13616      * </code></pre>
13617      */
13618     getSortState : function(){
13619         return this.sortInfo;
13620     },
13621
13622     // private
13623     applySort : function(){
13624         if(this.sortInfo && !this.remoteSort){
13625             var s = this.sortInfo, f = s.field;
13626             var st = this.fields.get(f).sortType;
13627             var fn = function(r1, r2){
13628                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13629                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13630             };
13631             this.data.sort(s.direction, fn);
13632             if(this.snapshot && this.snapshot != this.data){
13633                 this.snapshot.sort(s.direction, fn);
13634             }
13635         }
13636     },
13637
13638     /**
13639      * Sets the default sort column and order to be used by the next load operation.
13640      * @param {String} fieldName The name of the field to sort by.
13641      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13642      */
13643     setDefaultSort : function(field, dir){
13644         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13645     },
13646
13647     /**
13648      * Sort the Records.
13649      * If remote sorting is used, the sort is performed on the server, and the cache is
13650      * reloaded. If local sorting is used, the cache is sorted internally.
13651      * @param {String} fieldName The name of the field to sort by.
13652      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13653      */
13654     sort : function(fieldName, dir){
13655         var f = this.fields.get(fieldName);
13656         if(!dir){
13657             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13658             
13659             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13660                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13661             }else{
13662                 dir = f.sortDir;
13663             }
13664         }
13665         this.sortToggle[f.name] = dir;
13666         this.sortInfo = {field: f.name, direction: dir};
13667         if(!this.remoteSort){
13668             this.applySort();
13669             this.fireEvent("datachanged", this);
13670         }else{
13671             this.load(this.lastOptions);
13672         }
13673     },
13674
13675     /**
13676      * Calls the specified function for each of the Records in the cache.
13677      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13678      * Returning <em>false</em> aborts and exits the iteration.
13679      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13680      */
13681     each : function(fn, scope){
13682         this.data.each(fn, scope);
13683     },
13684
13685     /**
13686      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13687      * (e.g., during paging).
13688      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13689      */
13690     getModifiedRecords : function(){
13691         return this.modified;
13692     },
13693
13694     // private
13695     createFilterFn : function(property, value, anyMatch){
13696         if(!value.exec){ // not a regex
13697             value = String(value);
13698             if(value.length == 0){
13699                 return false;
13700             }
13701             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13702         }
13703         return function(r){
13704             return value.test(r.data[property]);
13705         };
13706     },
13707
13708     /**
13709      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13710      * @param {String} property A field on your records
13711      * @param {Number} start The record index to start at (defaults to 0)
13712      * @param {Number} end The last record index to include (defaults to length - 1)
13713      * @return {Number} The sum
13714      */
13715     sum : function(property, start, end){
13716         var rs = this.data.items, v = 0;
13717         start = start || 0;
13718         end = (end || end === 0) ? end : rs.length-1;
13719
13720         for(var i = start; i <= end; i++){
13721             v += (rs[i].data[property] || 0);
13722         }
13723         return v;
13724     },
13725
13726     /**
13727      * Filter the records by a specified property.
13728      * @param {String} field A field on your records
13729      * @param {String/RegExp} value Either a string that the field
13730      * should start with or a RegExp to test against the field
13731      * @param {Boolean} anyMatch True to match any part not just the beginning
13732      */
13733     filter : function(property, value, anyMatch){
13734         var fn = this.createFilterFn(property, value, anyMatch);
13735         return fn ? this.filterBy(fn) : this.clearFilter();
13736     },
13737
13738     /**
13739      * Filter by a function. The specified function will be called with each
13740      * record in this data source. If the function returns true the record is included,
13741      * otherwise it is filtered.
13742      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13743      * @param {Object} scope (optional) The scope of the function (defaults to this)
13744      */
13745     filterBy : function(fn, scope){
13746         this.snapshot = this.snapshot || this.data;
13747         this.data = this.queryBy(fn, scope||this);
13748         this.fireEvent("datachanged", this);
13749     },
13750
13751     /**
13752      * Query the records by a specified property.
13753      * @param {String} field A field on your records
13754      * @param {String/RegExp} value Either a string that the field
13755      * should start with or a RegExp to test against the field
13756      * @param {Boolean} anyMatch True to match any part not just the beginning
13757      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13758      */
13759     query : function(property, value, anyMatch){
13760         var fn = this.createFilterFn(property, value, anyMatch);
13761         return fn ? this.queryBy(fn) : this.data.clone();
13762     },
13763
13764     /**
13765      * Query by a function. The specified function will be called with each
13766      * record in this data source. If the function returns true the record is included
13767      * in the results.
13768      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13769      * @param {Object} scope (optional) The scope of the function (defaults to this)
13770       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13771      **/
13772     queryBy : function(fn, scope){
13773         var data = this.snapshot || this.data;
13774         return data.filterBy(fn, scope||this);
13775     },
13776
13777     /**
13778      * Collects unique values for a particular dataIndex from this store.
13779      * @param {String} dataIndex The property to collect
13780      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13781      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13782      * @return {Array} An array of the unique values
13783      **/
13784     collect : function(dataIndex, allowNull, bypassFilter){
13785         var d = (bypassFilter === true && this.snapshot) ?
13786                 this.snapshot.items : this.data.items;
13787         var v, sv, r = [], l = {};
13788         for(var i = 0, len = d.length; i < len; i++){
13789             v = d[i].data[dataIndex];
13790             sv = String(v);
13791             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13792                 l[sv] = true;
13793                 r[r.length] = v;
13794             }
13795         }
13796         return r;
13797     },
13798
13799     /**
13800      * Revert to a view of the Record cache with no filtering applied.
13801      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13802      */
13803     clearFilter : function(suppressEvent){
13804         if(this.snapshot && this.snapshot != this.data){
13805             this.data = this.snapshot;
13806             delete this.snapshot;
13807             if(suppressEvent !== true){
13808                 this.fireEvent("datachanged", this);
13809             }
13810         }
13811     },
13812
13813     // private
13814     afterEdit : function(record){
13815         if(this.modified.indexOf(record) == -1){
13816             this.modified.push(record);
13817         }
13818         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13819     },
13820     
13821     // private
13822     afterReject : function(record){
13823         this.modified.remove(record);
13824         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13825     },
13826
13827     // private
13828     afterCommit : function(record){
13829         this.modified.remove(record);
13830         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13831     },
13832
13833     /**
13834      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13835      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13836      */
13837     commitChanges : function(){
13838         var m = this.modified.slice(0);
13839         this.modified = [];
13840         for(var i = 0, len = m.length; i < len; i++){
13841             m[i].commit();
13842         }
13843     },
13844
13845     /**
13846      * Cancel outstanding changes on all changed records.
13847      */
13848     rejectChanges : function(){
13849         var m = this.modified.slice(0);
13850         this.modified = [];
13851         for(var i = 0, len = m.length; i < len; i++){
13852             m[i].reject();
13853         }
13854     },
13855
13856     onMetaChange : function(meta, rtype, o){
13857         this.recordType = rtype;
13858         this.fields = rtype.prototype.fields;
13859         delete this.snapshot;
13860         this.sortInfo = meta.sortInfo || this.sortInfo;
13861         this.modified = [];
13862         this.fireEvent('metachange', this, this.reader.meta);
13863     },
13864     
13865     moveIndex : function(data, type)
13866     {
13867         var index = this.indexOf(data);
13868         
13869         var newIndex = index + type;
13870         
13871         this.remove(data);
13872         
13873         this.insert(newIndex, data);
13874         
13875     }
13876 });/*
13877  * Based on:
13878  * Ext JS Library 1.1.1
13879  * Copyright(c) 2006-2007, Ext JS, LLC.
13880  *
13881  * Originally Released Under LGPL - original licence link has changed is not relivant.
13882  *
13883  * Fork - LGPL
13884  * <script type="text/javascript">
13885  */
13886
13887 /**
13888  * @class Roo.data.SimpleStore
13889  * @extends Roo.data.Store
13890  * Small helper class to make creating Stores from Array data easier.
13891  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13892  * @cfg {Array} fields An array of field definition objects, or field name strings.
13893  * @cfg {Object} an existing reader (eg. copied from another store)
13894  * @cfg {Array} data The multi-dimensional array of data
13895  * @constructor
13896  * @param {Object} config
13897  */
13898 Roo.data.SimpleStore = function(config)
13899 {
13900     Roo.data.SimpleStore.superclass.constructor.call(this, {
13901         isLocal : true,
13902         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13903                 id: config.id
13904             },
13905             Roo.data.Record.create(config.fields)
13906         ),
13907         proxy : new Roo.data.MemoryProxy(config.data)
13908     });
13909     this.load();
13910 };
13911 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13912  * Based on:
13913  * Ext JS Library 1.1.1
13914  * Copyright(c) 2006-2007, Ext JS, LLC.
13915  *
13916  * Originally Released Under LGPL - original licence link has changed is not relivant.
13917  *
13918  * Fork - LGPL
13919  * <script type="text/javascript">
13920  */
13921
13922 /**
13923 /**
13924  * @extends Roo.data.Store
13925  * @class Roo.data.JsonStore
13926  * Small helper class to make creating Stores for JSON data easier. <br/>
13927 <pre><code>
13928 var store = new Roo.data.JsonStore({
13929     url: 'get-images.php',
13930     root: 'images',
13931     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13932 });
13933 </code></pre>
13934  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13935  * JsonReader and HttpProxy (unless inline data is provided).</b>
13936  * @cfg {Array} fields An array of field definition objects, or field name strings.
13937  * @constructor
13938  * @param {Object} config
13939  */
13940 Roo.data.JsonStore = function(c){
13941     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13942         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13943         reader: new Roo.data.JsonReader(c, c.fields)
13944     }));
13945 };
13946 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13947  * Based on:
13948  * Ext JS Library 1.1.1
13949  * Copyright(c) 2006-2007, Ext JS, LLC.
13950  *
13951  * Originally Released Under LGPL - original licence link has changed is not relivant.
13952  *
13953  * Fork - LGPL
13954  * <script type="text/javascript">
13955  */
13956
13957  
13958 Roo.data.Field = function(config){
13959     if(typeof config == "string"){
13960         config = {name: config};
13961     }
13962     Roo.apply(this, config);
13963     
13964     if(!this.type){
13965         this.type = "auto";
13966     }
13967     
13968     var st = Roo.data.SortTypes;
13969     // named sortTypes are supported, here we look them up
13970     if(typeof this.sortType == "string"){
13971         this.sortType = st[this.sortType];
13972     }
13973     
13974     // set default sortType for strings and dates
13975     if(!this.sortType){
13976         switch(this.type){
13977             case "string":
13978                 this.sortType = st.asUCString;
13979                 break;
13980             case "date":
13981                 this.sortType = st.asDate;
13982                 break;
13983             default:
13984                 this.sortType = st.none;
13985         }
13986     }
13987
13988     // define once
13989     var stripRe = /[\$,%]/g;
13990
13991     // prebuilt conversion function for this field, instead of
13992     // switching every time we're reading a value
13993     if(!this.convert){
13994         var cv, dateFormat = this.dateFormat;
13995         switch(this.type){
13996             case "":
13997             case "auto":
13998             case undefined:
13999                 cv = function(v){ return v; };
14000                 break;
14001             case "string":
14002                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14003                 break;
14004             case "int":
14005                 cv = function(v){
14006                     return v !== undefined && v !== null && v !== '' ?
14007                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14008                     };
14009                 break;
14010             case "float":
14011                 cv = function(v){
14012                     return v !== undefined && v !== null && v !== '' ?
14013                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14014                     };
14015                 break;
14016             case "bool":
14017             case "boolean":
14018                 cv = function(v){ return v === true || v === "true" || v == 1; };
14019                 break;
14020             case "date":
14021                 cv = function(v){
14022                     if(!v){
14023                         return '';
14024                     }
14025                     if(v instanceof Date){
14026                         return v;
14027                     }
14028                     if(dateFormat){
14029                         if(dateFormat == "timestamp"){
14030                             return new Date(v*1000);
14031                         }
14032                         return Date.parseDate(v, dateFormat);
14033                     }
14034                     var parsed = Date.parse(v);
14035                     return parsed ? new Date(parsed) : null;
14036                 };
14037              break;
14038             
14039         }
14040         this.convert = cv;
14041     }
14042 };
14043
14044 Roo.data.Field.prototype = {
14045     dateFormat: null,
14046     defaultValue: "",
14047     mapping: null,
14048     sortType : null,
14049     sortDir : "ASC"
14050 };/*
14051  * Based on:
14052  * Ext JS Library 1.1.1
14053  * Copyright(c) 2006-2007, Ext JS, LLC.
14054  *
14055  * Originally Released Under LGPL - original licence link has changed is not relivant.
14056  *
14057  * Fork - LGPL
14058  * <script type="text/javascript">
14059  */
14060  
14061 // Base class for reading structured data from a data source.  This class is intended to be
14062 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14063
14064 /**
14065  * @class Roo.data.DataReader
14066  * Base class for reading structured data from a data source.  This class is intended to be
14067  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14068  */
14069
14070 Roo.data.DataReader = function(meta, recordType){
14071     
14072     this.meta = meta;
14073     
14074     this.recordType = recordType instanceof Array ? 
14075         Roo.data.Record.create(recordType) : recordType;
14076 };
14077
14078 Roo.data.DataReader.prototype = {
14079     
14080     
14081     readerType : 'Data',
14082      /**
14083      * Create an empty record
14084      * @param {Object} data (optional) - overlay some values
14085      * @return {Roo.data.Record} record created.
14086      */
14087     newRow :  function(d) {
14088         var da =  {};
14089         this.recordType.prototype.fields.each(function(c) {
14090             switch( c.type) {
14091                 case 'int' : da[c.name] = 0; break;
14092                 case 'date' : da[c.name] = new Date(); break;
14093                 case 'float' : da[c.name] = 0.0; break;
14094                 case 'boolean' : da[c.name] = false; break;
14095                 default : da[c.name] = ""; break;
14096             }
14097             
14098         });
14099         return new this.recordType(Roo.apply(da, d));
14100     }
14101     
14102     
14103 };/*
14104  * Based on:
14105  * Ext JS Library 1.1.1
14106  * Copyright(c) 2006-2007, Ext JS, LLC.
14107  *
14108  * Originally Released Under LGPL - original licence link has changed is not relivant.
14109  *
14110  * Fork - LGPL
14111  * <script type="text/javascript">
14112  */
14113
14114 /**
14115  * @class Roo.data.DataProxy
14116  * @extends Roo.data.Observable
14117  * This class is an abstract base class for implementations which provide retrieval of
14118  * unformatted data objects.<br>
14119  * <p>
14120  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14121  * (of the appropriate type which knows how to parse the data object) to provide a block of
14122  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14123  * <p>
14124  * Custom implementations must implement the load method as described in
14125  * {@link Roo.data.HttpProxy#load}.
14126  */
14127 Roo.data.DataProxy = function(){
14128     this.addEvents({
14129         /**
14130          * @event beforeload
14131          * Fires before a network request is made to retrieve a data object.
14132          * @param {Object} This DataProxy object.
14133          * @param {Object} params The params parameter to the load function.
14134          */
14135         beforeload : true,
14136         /**
14137          * @event load
14138          * Fires before the load method's callback is called.
14139          * @param {Object} This DataProxy object.
14140          * @param {Object} o The data object.
14141          * @param {Object} arg The callback argument object passed to the load function.
14142          */
14143         load : true,
14144         /**
14145          * @event loadexception
14146          * Fires if an Exception occurs during data retrieval.
14147          * @param {Object} This DataProxy object.
14148          * @param {Object} o The data object.
14149          * @param {Object} arg The callback argument object passed to the load function.
14150          * @param {Object} e The Exception.
14151          */
14152         loadexception : true
14153     });
14154     Roo.data.DataProxy.superclass.constructor.call(this);
14155 };
14156
14157 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14158
14159     /**
14160      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14161      */
14162 /*
14163  * Based on:
14164  * Ext JS Library 1.1.1
14165  * Copyright(c) 2006-2007, Ext JS, LLC.
14166  *
14167  * Originally Released Under LGPL - original licence link has changed is not relivant.
14168  *
14169  * Fork - LGPL
14170  * <script type="text/javascript">
14171  */
14172 /**
14173  * @class Roo.data.MemoryProxy
14174  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14175  * to the Reader when its load method is called.
14176  * @constructor
14177  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14178  */
14179 Roo.data.MemoryProxy = function(data){
14180     if (data.data) {
14181         data = data.data;
14182     }
14183     Roo.data.MemoryProxy.superclass.constructor.call(this);
14184     this.data = data;
14185 };
14186
14187 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14188     
14189     /**
14190      * Load data from the requested source (in this case an in-memory
14191      * data object passed to the constructor), read the data object into
14192      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14193      * process that block using the passed callback.
14194      * @param {Object} params This parameter is not used by the MemoryProxy class.
14195      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14196      * object into a block of Roo.data.Records.
14197      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14198      * The function must be passed <ul>
14199      * <li>The Record block object</li>
14200      * <li>The "arg" argument from the load function</li>
14201      * <li>A boolean success indicator</li>
14202      * </ul>
14203      * @param {Object} scope The scope in which to call the callback
14204      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14205      */
14206     load : function(params, reader, callback, scope, arg){
14207         params = params || {};
14208         var result;
14209         try {
14210             result = reader.readRecords(params.data ? params.data :this.data);
14211         }catch(e){
14212             this.fireEvent("loadexception", this, arg, null, e);
14213             callback.call(scope, null, arg, false);
14214             return;
14215         }
14216         callback.call(scope, result, arg, true);
14217     },
14218     
14219     // private
14220     update : function(params, records){
14221         
14222     }
14223 });/*
14224  * Based on:
14225  * Ext JS Library 1.1.1
14226  * Copyright(c) 2006-2007, Ext JS, LLC.
14227  *
14228  * Originally Released Under LGPL - original licence link has changed is not relivant.
14229  *
14230  * Fork - LGPL
14231  * <script type="text/javascript">
14232  */
14233 /**
14234  * @class Roo.data.HttpProxy
14235  * @extends Roo.data.DataProxy
14236  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14237  * configured to reference a certain URL.<br><br>
14238  * <p>
14239  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14240  * from which the running page was served.<br><br>
14241  * <p>
14242  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14243  * <p>
14244  * Be aware that to enable the browser to parse an XML document, the server must set
14245  * the Content-Type header in the HTTP response to "text/xml".
14246  * @constructor
14247  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14248  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14249  * will be used to make the request.
14250  */
14251 Roo.data.HttpProxy = function(conn){
14252     Roo.data.HttpProxy.superclass.constructor.call(this);
14253     // is conn a conn config or a real conn?
14254     this.conn = conn;
14255     this.useAjax = !conn || !conn.events;
14256   
14257 };
14258
14259 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14260     // thse are take from connection...
14261     
14262     /**
14263      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14264      */
14265     /**
14266      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14267      * extra parameters to each request made by this object. (defaults to undefined)
14268      */
14269     /**
14270      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14271      *  to each request made by this object. (defaults to undefined)
14272      */
14273     /**
14274      * @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)
14275      */
14276     /**
14277      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14278      */
14279      /**
14280      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14281      * @type Boolean
14282      */
14283   
14284
14285     /**
14286      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14287      * @type Boolean
14288      */
14289     /**
14290      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14291      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14292      * a finer-grained basis than the DataProxy events.
14293      */
14294     getConnection : function(){
14295         return this.useAjax ? Roo.Ajax : this.conn;
14296     },
14297
14298     /**
14299      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14300      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14301      * process that block using the passed callback.
14302      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14303      * for the request to the remote server.
14304      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14305      * object into a block of Roo.data.Records.
14306      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14307      * The function must be passed <ul>
14308      * <li>The Record block object</li>
14309      * <li>The "arg" argument from the load function</li>
14310      * <li>A boolean success indicator</li>
14311      * </ul>
14312      * @param {Object} scope The scope in which to call the callback
14313      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14314      */
14315     load : function(params, reader, callback, scope, arg){
14316         if(this.fireEvent("beforeload", this, params) !== false){
14317             var  o = {
14318                 params : params || {},
14319                 request: {
14320                     callback : callback,
14321                     scope : scope,
14322                     arg : arg
14323                 },
14324                 reader: reader,
14325                 callback : this.loadResponse,
14326                 scope: this
14327             };
14328             if(this.useAjax){
14329                 Roo.applyIf(o, this.conn);
14330                 if(this.activeRequest){
14331                     Roo.Ajax.abort(this.activeRequest);
14332                 }
14333                 this.activeRequest = Roo.Ajax.request(o);
14334             }else{
14335                 this.conn.request(o);
14336             }
14337         }else{
14338             callback.call(scope||this, null, arg, false);
14339         }
14340     },
14341
14342     // private
14343     loadResponse : function(o, success, response){
14344         delete this.activeRequest;
14345         if(!success){
14346             this.fireEvent("loadexception", this, o, response);
14347             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14348             return;
14349         }
14350         var result;
14351         try {
14352             result = o.reader.read(response);
14353         }catch(e){
14354             this.fireEvent("loadexception", this, o, response, e);
14355             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14356             return;
14357         }
14358         
14359         this.fireEvent("load", this, o, o.request.arg);
14360         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14361     },
14362
14363     // private
14364     update : function(dataSet){
14365
14366     },
14367
14368     // private
14369     updateResponse : function(dataSet){
14370
14371     }
14372 });/*
14373  * Based on:
14374  * Ext JS Library 1.1.1
14375  * Copyright(c) 2006-2007, Ext JS, LLC.
14376  *
14377  * Originally Released Under LGPL - original licence link has changed is not relivant.
14378  *
14379  * Fork - LGPL
14380  * <script type="text/javascript">
14381  */
14382
14383 /**
14384  * @class Roo.data.ScriptTagProxy
14385  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14386  * other than the originating domain of the running page.<br><br>
14387  * <p>
14388  * <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
14389  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14390  * <p>
14391  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14392  * source code that is used as the source inside a &lt;script> tag.<br><br>
14393  * <p>
14394  * In order for the browser to process the returned data, the server must wrap the data object
14395  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14396  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14397  * depending on whether the callback name was passed:
14398  * <p>
14399  * <pre><code>
14400 boolean scriptTag = false;
14401 String cb = request.getParameter("callback");
14402 if (cb != null) {
14403     scriptTag = true;
14404     response.setContentType("text/javascript");
14405 } else {
14406     response.setContentType("application/x-json");
14407 }
14408 Writer out = response.getWriter();
14409 if (scriptTag) {
14410     out.write(cb + "(");
14411 }
14412 out.print(dataBlock.toJsonString());
14413 if (scriptTag) {
14414     out.write(");");
14415 }
14416 </pre></code>
14417  *
14418  * @constructor
14419  * @param {Object} config A configuration object.
14420  */
14421 Roo.data.ScriptTagProxy = function(config){
14422     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14423     Roo.apply(this, config);
14424     this.head = document.getElementsByTagName("head")[0];
14425 };
14426
14427 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14428
14429 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14430     /**
14431      * @cfg {String} url The URL from which to request the data object.
14432      */
14433     /**
14434      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14435      */
14436     timeout : 30000,
14437     /**
14438      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14439      * the server the name of the callback function set up by the load call to process the returned data object.
14440      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14441      * javascript output which calls this named function passing the data object as its only parameter.
14442      */
14443     callbackParam : "callback",
14444     /**
14445      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14446      * name to the request.
14447      */
14448     nocache : true,
14449
14450     /**
14451      * Load data from the configured URL, read the data object into
14452      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14453      * process that block using the passed callback.
14454      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14455      * for the request to the remote server.
14456      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14457      * object into a block of Roo.data.Records.
14458      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14459      * The function must be passed <ul>
14460      * <li>The Record block object</li>
14461      * <li>The "arg" argument from the load function</li>
14462      * <li>A boolean success indicator</li>
14463      * </ul>
14464      * @param {Object} scope The scope in which to call the callback
14465      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14466      */
14467     load : function(params, reader, callback, scope, arg){
14468         if(this.fireEvent("beforeload", this, params) !== false){
14469
14470             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14471
14472             var url = this.url;
14473             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14474             if(this.nocache){
14475                 url += "&_dc=" + (new Date().getTime());
14476             }
14477             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14478             var trans = {
14479                 id : transId,
14480                 cb : "stcCallback"+transId,
14481                 scriptId : "stcScript"+transId,
14482                 params : params,
14483                 arg : arg,
14484                 url : url,
14485                 callback : callback,
14486                 scope : scope,
14487                 reader : reader
14488             };
14489             var conn = this;
14490
14491             window[trans.cb] = function(o){
14492                 conn.handleResponse(o, trans);
14493             };
14494
14495             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14496
14497             if(this.autoAbort !== false){
14498                 this.abort();
14499             }
14500
14501             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14502
14503             var script = document.createElement("script");
14504             script.setAttribute("src", url);
14505             script.setAttribute("type", "text/javascript");
14506             script.setAttribute("id", trans.scriptId);
14507             this.head.appendChild(script);
14508
14509             this.trans = trans;
14510         }else{
14511             callback.call(scope||this, null, arg, false);
14512         }
14513     },
14514
14515     // private
14516     isLoading : function(){
14517         return this.trans ? true : false;
14518     },
14519
14520     /**
14521      * Abort the current server request.
14522      */
14523     abort : function(){
14524         if(this.isLoading()){
14525             this.destroyTrans(this.trans);
14526         }
14527     },
14528
14529     // private
14530     destroyTrans : function(trans, isLoaded){
14531         this.head.removeChild(document.getElementById(trans.scriptId));
14532         clearTimeout(trans.timeoutId);
14533         if(isLoaded){
14534             window[trans.cb] = undefined;
14535             try{
14536                 delete window[trans.cb];
14537             }catch(e){}
14538         }else{
14539             // if hasn't been loaded, wait for load to remove it to prevent script error
14540             window[trans.cb] = function(){
14541                 window[trans.cb] = undefined;
14542                 try{
14543                     delete window[trans.cb];
14544                 }catch(e){}
14545             };
14546         }
14547     },
14548
14549     // private
14550     handleResponse : function(o, trans){
14551         this.trans = false;
14552         this.destroyTrans(trans, true);
14553         var result;
14554         try {
14555             result = trans.reader.readRecords(o);
14556         }catch(e){
14557             this.fireEvent("loadexception", this, o, trans.arg, e);
14558             trans.callback.call(trans.scope||window, null, trans.arg, false);
14559             return;
14560         }
14561         this.fireEvent("load", this, o, trans.arg);
14562         trans.callback.call(trans.scope||window, result, trans.arg, true);
14563     },
14564
14565     // private
14566     handleFailure : function(trans){
14567         this.trans = false;
14568         this.destroyTrans(trans, false);
14569         this.fireEvent("loadexception", this, null, trans.arg);
14570         trans.callback.call(trans.scope||window, null, trans.arg, false);
14571     }
14572 });/*
14573  * Based on:
14574  * Ext JS Library 1.1.1
14575  * Copyright(c) 2006-2007, Ext JS, LLC.
14576  *
14577  * Originally Released Under LGPL - original licence link has changed is not relivant.
14578  *
14579  * Fork - LGPL
14580  * <script type="text/javascript">
14581  */
14582
14583 /**
14584  * @class Roo.data.JsonReader
14585  * @extends Roo.data.DataReader
14586  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14587  * based on mappings in a provided Roo.data.Record constructor.
14588  * 
14589  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14590  * in the reply previously. 
14591  * 
14592  * <p>
14593  * Example code:
14594  * <pre><code>
14595 var RecordDef = Roo.data.Record.create([
14596     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14597     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14598 ]);
14599 var myReader = new Roo.data.JsonReader({
14600     totalProperty: "results",    // The property which contains the total dataset size (optional)
14601     root: "rows",                // The property which contains an Array of row objects
14602     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14603 }, RecordDef);
14604 </code></pre>
14605  * <p>
14606  * This would consume a JSON file like this:
14607  * <pre><code>
14608 { 'results': 2, 'rows': [
14609     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14610     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14611 }
14612 </code></pre>
14613  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14614  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14615  * paged from the remote server.
14616  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14617  * @cfg {String} root name of the property which contains the Array of row objects.
14618  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14619  * @cfg {Array} fields Array of field definition objects
14620  * @constructor
14621  * Create a new JsonReader
14622  * @param {Object} meta Metadata configuration options
14623  * @param {Object} recordType Either an Array of field definition objects,
14624  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14625  */
14626 Roo.data.JsonReader = function(meta, recordType){
14627     
14628     meta = meta || {};
14629     // set some defaults:
14630     Roo.applyIf(meta, {
14631         totalProperty: 'total',
14632         successProperty : 'success',
14633         root : 'data',
14634         id : 'id'
14635     });
14636     
14637     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14638 };
14639 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14640     
14641     readerType : 'Json',
14642     
14643     /**
14644      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14645      * Used by Store query builder to append _requestMeta to params.
14646      * 
14647      */
14648     metaFromRemote : false,
14649     /**
14650      * This method is only used by a DataProxy which has retrieved data from a remote server.
14651      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14652      * @return {Object} data A data block which is used by an Roo.data.Store object as
14653      * a cache of Roo.data.Records.
14654      */
14655     read : function(response){
14656         var json = response.responseText;
14657        
14658         var o = /* eval:var:o */ eval("("+json+")");
14659         if(!o) {
14660             throw {message: "JsonReader.read: Json object not found"};
14661         }
14662         
14663         if(o.metaData){
14664             
14665             delete this.ef;
14666             this.metaFromRemote = true;
14667             this.meta = o.metaData;
14668             this.recordType = Roo.data.Record.create(o.metaData.fields);
14669             this.onMetaChange(this.meta, this.recordType, o);
14670         }
14671         return this.readRecords(o);
14672     },
14673
14674     // private function a store will implement
14675     onMetaChange : function(meta, recordType, o){
14676
14677     },
14678
14679     /**
14680          * @ignore
14681          */
14682     simpleAccess: function(obj, subsc) {
14683         return obj[subsc];
14684     },
14685
14686         /**
14687          * @ignore
14688          */
14689     getJsonAccessor: function(){
14690         var re = /[\[\.]/;
14691         return function(expr) {
14692             try {
14693                 return(re.test(expr))
14694                     ? new Function("obj", "return obj." + expr)
14695                     : function(obj){
14696                         return obj[expr];
14697                     };
14698             } catch(e){}
14699             return Roo.emptyFn;
14700         };
14701     }(),
14702
14703     /**
14704      * Create a data block containing Roo.data.Records from an XML document.
14705      * @param {Object} o An object which contains an Array of row objects in the property specified
14706      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14707      * which contains the total size of the dataset.
14708      * @return {Object} data A data block which is used by an Roo.data.Store object as
14709      * a cache of Roo.data.Records.
14710      */
14711     readRecords : function(o){
14712         /**
14713          * After any data loads, the raw JSON data is available for further custom processing.
14714          * @type Object
14715          */
14716         this.o = o;
14717         var s = this.meta, Record = this.recordType,
14718             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14719
14720 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14721         if (!this.ef) {
14722             if(s.totalProperty) {
14723                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14724                 }
14725                 if(s.successProperty) {
14726                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14727                 }
14728                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14729                 if (s.id) {
14730                         var g = this.getJsonAccessor(s.id);
14731                         this.getId = function(rec) {
14732                                 var r = g(rec);  
14733                                 return (r === undefined || r === "") ? null : r;
14734                         };
14735                 } else {
14736                         this.getId = function(){return null;};
14737                 }
14738             this.ef = [];
14739             for(var jj = 0; jj < fl; jj++){
14740                 f = fi[jj];
14741                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14742                 this.ef[jj] = this.getJsonAccessor(map);
14743             }
14744         }
14745
14746         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14747         if(s.totalProperty){
14748             var vt = parseInt(this.getTotal(o), 10);
14749             if(!isNaN(vt)){
14750                 totalRecords = vt;
14751             }
14752         }
14753         if(s.successProperty){
14754             var vs = this.getSuccess(o);
14755             if(vs === false || vs === 'false'){
14756                 success = false;
14757             }
14758         }
14759         var records = [];
14760         for(var i = 0; i < c; i++){
14761                 var n = root[i];
14762             var values = {};
14763             var id = this.getId(n);
14764             for(var j = 0; j < fl; j++){
14765                 f = fi[j];
14766             var v = this.ef[j](n);
14767             if (!f.convert) {
14768                 Roo.log('missing convert for ' + f.name);
14769                 Roo.log(f);
14770                 continue;
14771             }
14772             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14773             }
14774             var record = new Record(values, id);
14775             record.json = n;
14776             records[i] = record;
14777         }
14778         return {
14779             raw : o,
14780             success : success,
14781             records : records,
14782             totalRecords : totalRecords
14783         };
14784     },
14785     // used when loading children.. @see loadDataFromChildren
14786     toLoadData: function(rec)
14787     {
14788         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14789         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14790         return { data : data, total : data.length };
14791         
14792     }
14793 });/*
14794  * Based on:
14795  * Ext JS Library 1.1.1
14796  * Copyright(c) 2006-2007, Ext JS, LLC.
14797  *
14798  * Originally Released Under LGPL - original licence link has changed is not relivant.
14799  *
14800  * Fork - LGPL
14801  * <script type="text/javascript">
14802  */
14803
14804 /**
14805  * @class Roo.data.ArrayReader
14806  * @extends Roo.data.DataReader
14807  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14808  * Each element of that Array represents a row of data fields. The
14809  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14810  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14811  * <p>
14812  * Example code:.
14813  * <pre><code>
14814 var RecordDef = Roo.data.Record.create([
14815     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14816     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14817 ]);
14818 var myReader = new Roo.data.ArrayReader({
14819     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14820 }, RecordDef);
14821 </code></pre>
14822  * <p>
14823  * This would consume an Array like this:
14824  * <pre><code>
14825 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14826   </code></pre>
14827  
14828  * @constructor
14829  * Create a new JsonReader
14830  * @param {Object} meta Metadata configuration options.
14831  * @param {Object|Array} recordType Either an Array of field definition objects
14832  * 
14833  * @cfg {Array} fields Array of field definition objects
14834  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14835  * as specified to {@link Roo.data.Record#create},
14836  * or an {@link Roo.data.Record} object
14837  *
14838  * 
14839  * created using {@link Roo.data.Record#create}.
14840  */
14841 Roo.data.ArrayReader = function(meta, recordType)
14842 {    
14843     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14844 };
14845
14846 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14847     
14848       /**
14849      * Create a data block containing Roo.data.Records from an XML document.
14850      * @param {Object} o An Array of row objects which represents the dataset.
14851      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14852      * a cache of Roo.data.Records.
14853      */
14854     readRecords : function(o)
14855     {
14856         var sid = this.meta ? this.meta.id : null;
14857         var recordType = this.recordType, fields = recordType.prototype.fields;
14858         var records = [];
14859         var root = o;
14860         for(var i = 0; i < root.length; i++){
14861                 var n = root[i];
14862             var values = {};
14863             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14864             for(var j = 0, jlen = fields.length; j < jlen; j++){
14865                 var f = fields.items[j];
14866                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14867                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14868                 v = f.convert(v);
14869                 values[f.name] = v;
14870             }
14871             var record = new recordType(values, id);
14872             record.json = n;
14873             records[records.length] = record;
14874         }
14875         return {
14876             records : records,
14877             totalRecords : records.length
14878         };
14879     },
14880     // used when loading children.. @see loadDataFromChildren
14881     toLoadData: function(rec)
14882     {
14883         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14884         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14885         
14886     }
14887     
14888     
14889 });/*
14890  * - LGPL
14891  * * 
14892  */
14893
14894 /**
14895  * @class Roo.bootstrap.ComboBox
14896  * @extends Roo.bootstrap.TriggerField
14897  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14898  * @cfg {Boolean} append (true|false) default false
14899  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14900  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14901  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14902  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14903  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14904  * @cfg {Boolean} animate default true
14905  * @cfg {Boolean} emptyResultText only for touch device
14906  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14907  * @cfg {String} emptyTitle default ''
14908  * @cfg {Number} width fixed with? experimental
14909  * @constructor
14910  * Create a new ComboBox.
14911  * @param {Object} config Configuration options
14912  */
14913 Roo.bootstrap.ComboBox = function(config){
14914     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14915     this.addEvents({
14916         /**
14917          * @event expand
14918          * Fires when the dropdown list is expanded
14919         * @param {Roo.bootstrap.ComboBox} combo This combo box
14920         */
14921         'expand' : true,
14922         /**
14923          * @event collapse
14924          * Fires when the dropdown list is collapsed
14925         * @param {Roo.bootstrap.ComboBox} combo This combo box
14926         */
14927         'collapse' : true,
14928         /**
14929          * @event beforeselect
14930          * Fires before a list item is selected. Return false to cancel the selection.
14931         * @param {Roo.bootstrap.ComboBox} combo This combo box
14932         * @param {Roo.data.Record} record The data record returned from the underlying store
14933         * @param {Number} index The index of the selected item in the dropdown list
14934         */
14935         'beforeselect' : true,
14936         /**
14937          * @event select
14938          * Fires when a list item is selected
14939         * @param {Roo.bootstrap.ComboBox} combo This combo box
14940         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14941         * @param {Number} index The index of the selected item in the dropdown list
14942         */
14943         'select' : true,
14944         /**
14945          * @event beforequery
14946          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14947          * The event object passed has these properties:
14948         * @param {Roo.bootstrap.ComboBox} combo This combo box
14949         * @param {String} query The query
14950         * @param {Boolean} forceAll true to force "all" query
14951         * @param {Boolean} cancel true to cancel the query
14952         * @param {Object} e The query event object
14953         */
14954         'beforequery': true,
14955          /**
14956          * @event add
14957          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14958         * @param {Roo.bootstrap.ComboBox} combo This combo box
14959         */
14960         'add' : true,
14961         /**
14962          * @event edit
14963          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14964         * @param {Roo.bootstrap.ComboBox} combo This combo box
14965         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14966         */
14967         'edit' : true,
14968         /**
14969          * @event remove
14970          * Fires when the remove value from the combobox array
14971         * @param {Roo.bootstrap.ComboBox} combo This combo box
14972         */
14973         'remove' : true,
14974         /**
14975          * @event afterremove
14976          * Fires when the remove value from the combobox array
14977         * @param {Roo.bootstrap.ComboBox} combo This combo box
14978         */
14979         'afterremove' : true,
14980         /**
14981          * @event specialfilter
14982          * Fires when specialfilter
14983             * @param {Roo.bootstrap.ComboBox} combo This combo box
14984             */
14985         'specialfilter' : true,
14986         /**
14987          * @event tick
14988          * Fires when tick the element
14989             * @param {Roo.bootstrap.ComboBox} combo This combo box
14990             */
14991         'tick' : true,
14992         /**
14993          * @event touchviewdisplay
14994          * Fires when touch view require special display (default is using displayField)
14995             * @param {Roo.bootstrap.ComboBox} combo This combo box
14996             * @param {Object} cfg set html .
14997             */
14998         'touchviewdisplay' : true
14999         
15000     });
15001     
15002     this.item = [];
15003     this.tickItems = [];
15004     
15005     this.selectedIndex = -1;
15006     if(this.mode == 'local'){
15007         if(config.queryDelay === undefined){
15008             this.queryDelay = 10;
15009         }
15010         if(config.minChars === undefined){
15011             this.minChars = 0;
15012         }
15013     }
15014 };
15015
15016 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15017      
15018     /**
15019      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15020      * rendering into an Roo.Editor, defaults to false)
15021      */
15022     /**
15023      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15024      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15025      */
15026     /**
15027      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15028      */
15029     /**
15030      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15031      * the dropdown list (defaults to undefined, with no header element)
15032      */
15033
15034      /**
15035      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15036      */
15037      
15038      /**
15039      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15040      */
15041     listWidth: undefined,
15042     /**
15043      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15044      * mode = 'remote' or 'text' if mode = 'local')
15045      */
15046     displayField: undefined,
15047     
15048     /**
15049      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15050      * mode = 'remote' or 'value' if mode = 'local'). 
15051      * Note: use of a valueField requires the user make a selection
15052      * in order for a value to be mapped.
15053      */
15054     valueField: undefined,
15055     /**
15056      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15057      */
15058     modalTitle : '',
15059     
15060     /**
15061      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15062      * field's data value (defaults to the underlying DOM element's name)
15063      */
15064     hiddenName: undefined,
15065     /**
15066      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15067      */
15068     listClass: '',
15069     /**
15070      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15071      */
15072     selectedClass: 'active',
15073     
15074     /**
15075      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15076      */
15077     shadow:'sides',
15078     /**
15079      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15080      * anchor positions (defaults to 'tl-bl')
15081      */
15082     listAlign: 'tl-bl?',
15083     /**
15084      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15085      */
15086     maxHeight: 300,
15087     /**
15088      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15089      * query specified by the allQuery config option (defaults to 'query')
15090      */
15091     triggerAction: 'query',
15092     /**
15093      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15094      * (defaults to 4, does not apply if editable = false)
15095      */
15096     minChars : 4,
15097     /**
15098      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15099      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15100      */
15101     typeAhead: false,
15102     /**
15103      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15104      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15105      */
15106     queryDelay: 500,
15107     /**
15108      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15109      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15110      */
15111     pageSize: 0,
15112     /**
15113      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15114      * when editable = true (defaults to false)
15115      */
15116     selectOnFocus:false,
15117     /**
15118      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15119      */
15120     queryParam: 'query',
15121     /**
15122      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15123      * when mode = 'remote' (defaults to 'Loading...')
15124      */
15125     loadingText: 'Loading...',
15126     /**
15127      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15128      */
15129     resizable: false,
15130     /**
15131      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15132      */
15133     handleHeight : 8,
15134     /**
15135      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15136      * traditional select (defaults to true)
15137      */
15138     editable: true,
15139     /**
15140      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15141      */
15142     allQuery: '',
15143     /**
15144      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15145      */
15146     mode: 'remote',
15147     /**
15148      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15149      * listWidth has a higher value)
15150      */
15151     minListWidth : 70,
15152     /**
15153      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15154      * allow the user to set arbitrary text into the field (defaults to false)
15155      */
15156     forceSelection:false,
15157     /**
15158      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15159      * if typeAhead = true (defaults to 250)
15160      */
15161     typeAheadDelay : 250,
15162     /**
15163      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15164      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15165      */
15166     valueNotFoundText : undefined,
15167     /**
15168      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15169      */
15170     blockFocus : false,
15171     
15172     /**
15173      * @cfg {Boolean} disableClear Disable showing of clear button.
15174      */
15175     disableClear : false,
15176     /**
15177      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15178      */
15179     alwaysQuery : false,
15180     
15181     /**
15182      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15183      */
15184     multiple : false,
15185     
15186     /**
15187      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     invalidClass : "has-warning",
15190     
15191     /**
15192      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15193      */
15194     validClass : "has-success",
15195     
15196     /**
15197      * @cfg {Boolean} specialFilter (true|false) special filter default false
15198      */
15199     specialFilter : false,
15200     
15201     /**
15202      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15203      */
15204     mobileTouchView : true,
15205     
15206     /**
15207      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15208      */
15209     useNativeIOS : false,
15210     
15211     /**
15212      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15213      */
15214     mobile_restrict_height : false,
15215     
15216     ios_options : false,
15217     
15218     //private
15219     addicon : false,
15220     editicon: false,
15221     
15222     page: 0,
15223     hasQuery: false,
15224     append: false,
15225     loadNext: false,
15226     autoFocus : true,
15227     tickable : false,
15228     btnPosition : 'right',
15229     triggerList : true,
15230     showToggleBtn : true,
15231     animate : true,
15232     emptyResultText: 'Empty',
15233     triggerText : 'Select',
15234     emptyTitle : '',
15235     width : false,
15236     
15237     // element that contains real text value.. (when hidden is used..)
15238     
15239     getAutoCreate : function()
15240     {   
15241         var cfg = false;
15242         //render
15243         /*
15244          * Render classic select for iso
15245          */
15246         
15247         if(Roo.isIOS && this.useNativeIOS){
15248             cfg = this.getAutoCreateNativeIOS();
15249             return cfg;
15250         }
15251         
15252         /*
15253          * Touch Devices
15254          */
15255         
15256         if(Roo.isTouch && this.mobileTouchView){
15257             cfg = this.getAutoCreateTouchView();
15258             return cfg;;
15259         }
15260         
15261         /*
15262          *  Normal ComboBox
15263          */
15264         if(!this.tickable){
15265             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15266             return cfg;
15267         }
15268         
15269         /*
15270          *  ComboBox with tickable selections
15271          */
15272              
15273         var align = this.labelAlign || this.parentLabelAlign();
15274         
15275         cfg = {
15276             cls : 'form-group roo-combobox-tickable' //input-group
15277         };
15278         
15279         var btn_text_select = '';
15280         var btn_text_done = '';
15281         var btn_text_cancel = '';
15282         
15283         if (this.btn_text_show) {
15284             btn_text_select = 'Select';
15285             btn_text_done = 'Done';
15286             btn_text_cancel = 'Cancel'; 
15287         }
15288         
15289         var buttons = {
15290             tag : 'div',
15291             cls : 'tickable-buttons',
15292             cn : [
15293                 {
15294                     tag : 'button',
15295                     type : 'button',
15296                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15297                     //html : this.triggerText
15298                     html: btn_text_select
15299                 },
15300                 {
15301                     tag : 'button',
15302                     type : 'button',
15303                     name : 'ok',
15304                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15305                     //html : 'Done'
15306                     html: btn_text_done
15307                 },
15308                 {
15309                     tag : 'button',
15310                     type : 'button',
15311                     name : 'cancel',
15312                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15313                     //html : 'Cancel'
15314                     html: btn_text_cancel
15315                 }
15316             ]
15317         };
15318         
15319         if(this.editable){
15320             buttons.cn.unshift({
15321                 tag: 'input',
15322                 cls: 'roo-select2-search-field-input'
15323             });
15324         }
15325         
15326         var _this = this;
15327         
15328         Roo.each(buttons.cn, function(c){
15329             if (_this.size) {
15330                 c.cls += ' btn-' + _this.size;
15331             }
15332
15333             if (_this.disabled) {
15334                 c.disabled = true;
15335             }
15336         });
15337         
15338         var box = {
15339             tag: 'div',
15340             style : 'display: contents',
15341             cn: [
15342                 {
15343                     tag: 'input',
15344                     type : 'hidden',
15345                     cls: 'form-hidden-field'
15346                 },
15347                 {
15348                     tag: 'ul',
15349                     cls: 'roo-select2-choices',
15350                     cn:[
15351                         {
15352                             tag: 'li',
15353                             cls: 'roo-select2-search-field',
15354                             cn: [
15355                                 buttons
15356                             ]
15357                         }
15358                     ]
15359                 }
15360             ]
15361         };
15362         
15363         var combobox = {
15364             cls: 'roo-select2-container input-group roo-select2-container-multi',
15365             cn: [
15366                 
15367                 box
15368 //                {
15369 //                    tag: 'ul',
15370 //                    cls: 'typeahead typeahead-long dropdown-menu',
15371 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15372 //                }
15373             ]
15374         };
15375         
15376         if(this.hasFeedback && !this.allowBlank){
15377             
15378             var feedback = {
15379                 tag: 'span',
15380                 cls: 'glyphicon form-control-feedback'
15381             };
15382
15383             combobox.cn.push(feedback);
15384         }
15385         
15386         
15387         
15388         var indicator = {
15389             tag : 'i',
15390             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15391             tooltip : 'This field is required'
15392         };
15393         if (Roo.bootstrap.version == 4) {
15394             indicator = {
15395                 tag : 'i',
15396                 style : 'display:none'
15397             };
15398         }
15399         if (align ==='left' && this.fieldLabel.length) {
15400             
15401             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15402             
15403             cfg.cn = [
15404                 indicator,
15405                 {
15406                     tag: 'label',
15407                     'for' :  id,
15408                     cls : 'control-label col-form-label',
15409                     html : this.fieldLabel
15410
15411                 },
15412                 {
15413                     cls : "", 
15414                     cn: [
15415                         combobox
15416                     ]
15417                 }
15418
15419             ];
15420             
15421             var labelCfg = cfg.cn[1];
15422             var contentCfg = cfg.cn[2];
15423             
15424
15425             if(this.indicatorpos == 'right'){
15426                 
15427                 cfg.cn = [
15428                     {
15429                         tag: 'label',
15430                         'for' :  id,
15431                         cls : 'control-label col-form-label',
15432                         cn : [
15433                             {
15434                                 tag : 'span',
15435                                 html : this.fieldLabel
15436                             },
15437                             indicator
15438                         ]
15439                     },
15440                     {
15441                         cls : "",
15442                         cn: [
15443                             combobox
15444                         ]
15445                     }
15446
15447                 ];
15448                 
15449                 
15450                 
15451                 labelCfg = cfg.cn[0];
15452                 contentCfg = cfg.cn[1];
15453             
15454             }
15455             
15456             if(this.labelWidth > 12){
15457                 labelCfg.style = "width: " + this.labelWidth + 'px';
15458             }
15459             if(this.width * 1 > 0){
15460                 contentCfg.style = "width: " + this.width + 'px';
15461             }
15462             if(this.labelWidth < 13 && this.labelmd == 0){
15463                 this.labelmd = this.labelWidth;
15464             }
15465             
15466             if(this.labellg > 0){
15467                 labelCfg.cls += ' col-lg-' + this.labellg;
15468                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15469             }
15470             
15471             if(this.labelmd > 0){
15472                 labelCfg.cls += ' col-md-' + this.labelmd;
15473                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15474             }
15475             
15476             if(this.labelsm > 0){
15477                 labelCfg.cls += ' col-sm-' + this.labelsm;
15478                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15479             }
15480             
15481             if(this.labelxs > 0){
15482                 labelCfg.cls += ' col-xs-' + this.labelxs;
15483                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15484             }
15485                 
15486                 
15487         } else if ( this.fieldLabel.length) {
15488 //                Roo.log(" label");
15489                  cfg.cn = [
15490                    indicator,
15491                     {
15492                         tag: 'label',
15493                         //cls : 'input-group-addon',
15494                         html : this.fieldLabel
15495                     },
15496                     combobox
15497                 ];
15498                 
15499                 if(this.indicatorpos == 'right'){
15500                     cfg.cn = [
15501                         {
15502                             tag: 'label',
15503                             //cls : 'input-group-addon',
15504                             html : this.fieldLabel
15505                         },
15506                         indicator,
15507                         combobox
15508                     ];
15509                     
15510                 }
15511
15512         } else {
15513             
15514 //                Roo.log(" no label && no align");
15515                 cfg = combobox
15516                      
15517                 
15518         }
15519          
15520         var settings=this;
15521         ['xs','sm','md','lg'].map(function(size){
15522             if (settings[size]) {
15523                 cfg.cls += ' col-' + size + '-' + settings[size];
15524             }
15525         });
15526         
15527         return cfg;
15528         
15529     },
15530     
15531     _initEventsCalled : false,
15532     
15533     // private
15534     initEvents: function()
15535     {   
15536         if (this._initEventsCalled) { // as we call render... prevent looping...
15537             return;
15538         }
15539         this._initEventsCalled = true;
15540         
15541         if (!this.store) {
15542             throw "can not find store for combo";
15543         }
15544         
15545         this.indicator = this.indicatorEl();
15546         
15547         this.store = Roo.factory(this.store, Roo.data);
15548         this.store.parent = this;
15549         
15550         // if we are building from html. then this element is so complex, that we can not really
15551         // use the rendered HTML.
15552         // so we have to trash and replace the previous code.
15553         if (Roo.XComponent.build_from_html) {
15554             // remove this element....
15555             var e = this.el.dom, k=0;
15556             while (e ) { e = e.previousSibling;  ++k;}
15557
15558             this.el.remove();
15559             
15560             this.el=false;
15561             this.rendered = false;
15562             
15563             this.render(this.parent().getChildContainer(true), k);
15564         }
15565         
15566         if(Roo.isIOS && this.useNativeIOS){
15567             this.initIOSView();
15568             return;
15569         }
15570         
15571         /*
15572          * Touch Devices
15573          */
15574         
15575         if(Roo.isTouch && this.mobileTouchView){
15576             this.initTouchView();
15577             return;
15578         }
15579         
15580         if(this.tickable){
15581             this.initTickableEvents();
15582             return;
15583         }
15584         
15585         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15586         
15587         if(this.hiddenName){
15588             
15589             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15590             
15591             this.hiddenField.dom.value =
15592                 this.hiddenValue !== undefined ? this.hiddenValue :
15593                 this.value !== undefined ? this.value : '';
15594
15595             // prevent input submission
15596             this.el.dom.removeAttribute('name');
15597             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15598              
15599              
15600         }
15601         //if(Roo.isGecko){
15602         //    this.el.dom.setAttribute('autocomplete', 'off');
15603         //}
15604         
15605         var cls = 'x-combo-list';
15606         
15607         //this.list = new Roo.Layer({
15608         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15609         //});
15610         
15611         var _this = this;
15612         
15613         (function(){
15614             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15615             _this.list.setWidth(lw);
15616         }).defer(100);
15617         
15618         this.list.on('mouseover', this.onViewOver, this);
15619         this.list.on('mousemove', this.onViewMove, this);
15620         this.list.on('scroll', this.onViewScroll, this);
15621         
15622         /*
15623         this.list.swallowEvent('mousewheel');
15624         this.assetHeight = 0;
15625
15626         if(this.title){
15627             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15628             this.assetHeight += this.header.getHeight();
15629         }
15630
15631         this.innerList = this.list.createChild({cls:cls+'-inner'});
15632         this.innerList.on('mouseover', this.onViewOver, this);
15633         this.innerList.on('mousemove', this.onViewMove, this);
15634         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15635         
15636         if(this.allowBlank && !this.pageSize && !this.disableClear){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.Toolbar(this.footer);
15639            
15640         }
15641         if(this.pageSize){
15642             this.footer = this.list.createChild({cls:cls+'-ft'});
15643             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15644                     {pageSize: this.pageSize});
15645             
15646         }
15647         
15648         if (this.pageTb && this.allowBlank && !this.disableClear) {
15649             var _this = this;
15650             this.pageTb.add(new Roo.Toolbar.Fill(), {
15651                 cls: 'x-btn-icon x-btn-clear',
15652                 text: '&#160;',
15653                 handler: function()
15654                 {
15655                     _this.collapse();
15656                     _this.clearValue();
15657                     _this.onSelect(false, -1);
15658                 }
15659             });
15660         }
15661         if (this.footer) {
15662             this.assetHeight += this.footer.getHeight();
15663         }
15664         */
15665             
15666         if(!this.tpl){
15667             this.tpl = Roo.bootstrap.version == 4 ?
15668                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15669                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15670         }
15671
15672         this.view = new Roo.View(this.list, this.tpl, {
15673             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15674         });
15675         //this.view.wrapEl.setDisplayed(false);
15676         this.view.on('click', this.onViewClick, this);
15677         
15678         
15679         this.store.on('beforeload', this.onBeforeLoad, this);
15680         this.store.on('load', this.onLoad, this);
15681         this.store.on('loadexception', this.onLoadException, this);
15682         /*
15683         if(this.resizable){
15684             this.resizer = new Roo.Resizable(this.list,  {
15685                pinned:true, handles:'se'
15686             });
15687             this.resizer.on('resize', function(r, w, h){
15688                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15689                 this.listWidth = w;
15690                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15691                 this.restrictHeight();
15692             }, this);
15693             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15694         }
15695         */
15696         if(!this.editable){
15697             this.editable = true;
15698             this.setEditable(false);
15699         }
15700         
15701         /*
15702         
15703         if (typeof(this.events.add.listeners) != 'undefined') {
15704             
15705             this.addicon = this.wrap.createChild(
15706                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15707        
15708             this.addicon.on('click', function(e) {
15709                 this.fireEvent('add', this);
15710             }, this);
15711         }
15712         if (typeof(this.events.edit.listeners) != 'undefined') {
15713             
15714             this.editicon = this.wrap.createChild(
15715                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15716             if (this.addicon) {
15717                 this.editicon.setStyle('margin-left', '40px');
15718             }
15719             this.editicon.on('click', function(e) {
15720                 
15721                 // we fire even  if inothing is selected..
15722                 this.fireEvent('edit', this, this.lastData );
15723                 
15724             }, this);
15725         }
15726         */
15727         
15728         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15729             "up" : function(e){
15730                 this.inKeyMode = true;
15731                 this.selectPrev();
15732             },
15733
15734             "down" : function(e){
15735                 if(!this.isExpanded()){
15736                     this.onTriggerClick();
15737                 }else{
15738                     this.inKeyMode = true;
15739                     this.selectNext();
15740                 }
15741             },
15742
15743             "enter" : function(e){
15744 //                this.onViewClick();
15745                 //return true;
15746                 this.collapse();
15747                 
15748                 if(this.fireEvent("specialkey", this, e)){
15749                     this.onViewClick(false);
15750                 }
15751                 
15752                 return true;
15753             },
15754
15755             "esc" : function(e){
15756                 this.collapse();
15757             },
15758
15759             "tab" : function(e){
15760                 this.collapse();
15761                 
15762                 if(this.fireEvent("specialkey", this, e)){
15763                     this.onViewClick(false);
15764                 }
15765                 
15766                 return true;
15767             },
15768
15769             scope : this,
15770
15771             doRelay : function(foo, bar, hname){
15772                 if(hname == 'down' || this.scope.isExpanded()){
15773                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15774                 }
15775                 return true;
15776             },
15777
15778             forceKeyDown: true
15779         });
15780         
15781         
15782         this.queryDelay = Math.max(this.queryDelay || 10,
15783                 this.mode == 'local' ? 10 : 250);
15784         
15785         
15786         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15787         
15788         if(this.typeAhead){
15789             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15790         }
15791         if(this.editable !== false){
15792             this.inputEl().on("keyup", this.onKeyUp, this);
15793         }
15794         if(this.forceSelection){
15795             this.inputEl().on('blur', this.doForce, this);
15796         }
15797         
15798         if(this.multiple){
15799             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15800             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15801         }
15802     },
15803     
15804     initTickableEvents: function()
15805     {   
15806         this.createList();
15807         
15808         if(this.hiddenName){
15809             
15810             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15811             
15812             this.hiddenField.dom.value =
15813                 this.hiddenValue !== undefined ? this.hiddenValue :
15814                 this.value !== undefined ? this.value : '';
15815
15816             // prevent input submission
15817             this.el.dom.removeAttribute('name');
15818             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15819              
15820              
15821         }
15822         
15823 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15824         
15825         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15826         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15827         if(this.triggerList){
15828             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15829         }
15830          
15831         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15832         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15833         
15834         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15835         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15836         
15837         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15838         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15839         
15840         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15841         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15842         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15843         
15844         this.okBtn.hide();
15845         this.cancelBtn.hide();
15846         
15847         var _this = this;
15848         
15849         (function(){
15850             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15851             _this.list.setWidth(lw);
15852         }).defer(100);
15853         
15854         this.list.on('mouseover', this.onViewOver, this);
15855         this.list.on('mousemove', this.onViewMove, this);
15856         
15857         this.list.on('scroll', this.onViewScroll, this);
15858         
15859         if(!this.tpl){
15860             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15861                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15862         }
15863
15864         this.view = new Roo.View(this.list, this.tpl, {
15865             singleSelect:true,
15866             tickable:true,
15867             parent:this,
15868             store: this.store,
15869             selectedClass: this.selectedClass
15870         });
15871         
15872         //this.view.wrapEl.setDisplayed(false);
15873         this.view.on('click', this.onViewClick, this);
15874         
15875         
15876         
15877         this.store.on('beforeload', this.onBeforeLoad, this);
15878         this.store.on('load', this.onLoad, this);
15879         this.store.on('loadexception', this.onLoadException, this);
15880         
15881         if(this.editable){
15882             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15883                 "up" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectPrev();
15886                 },
15887
15888                 "down" : function(e){
15889                     this.inKeyMode = true;
15890                     this.selectNext();
15891                 },
15892
15893                 "enter" : function(e){
15894                     if(this.fireEvent("specialkey", this, e)){
15895                         this.onViewClick(false);
15896                     }
15897                     
15898                     return true;
15899                 },
15900
15901                 "esc" : function(e){
15902                     this.onTickableFooterButtonClick(e, false, false);
15903                 },
15904
15905                 "tab" : function(e){
15906                     this.fireEvent("specialkey", this, e);
15907                     
15908                     this.onTickableFooterButtonClick(e, false, false);
15909                     
15910                     return true;
15911                 },
15912
15913                 scope : this,
15914
15915                 doRelay : function(e, fn, key){
15916                     if(this.scope.isExpanded()){
15917                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15918                     }
15919                     return true;
15920                 },
15921
15922                 forceKeyDown: true
15923             });
15924         }
15925         
15926         this.queryDelay = Math.max(this.queryDelay || 10,
15927                 this.mode == 'local' ? 10 : 250);
15928         
15929         
15930         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15931         
15932         if(this.typeAhead){
15933             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15934         }
15935         
15936         if(this.editable !== false){
15937             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15938         }
15939         
15940         this.indicator = this.indicatorEl();
15941         
15942         if(this.indicator){
15943             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15944             this.indicator.hide();
15945         }
15946         
15947     },
15948
15949     onDestroy : function(){
15950         if(this.view){
15951             this.view.setStore(null);
15952             this.view.el.removeAllListeners();
15953             this.view.el.remove();
15954             this.view.purgeListeners();
15955         }
15956         if(this.list){
15957             this.list.dom.innerHTML  = '';
15958         }
15959         
15960         if(this.store){
15961             this.store.un('beforeload', this.onBeforeLoad, this);
15962             this.store.un('load', this.onLoad, this);
15963             this.store.un('loadexception', this.onLoadException, this);
15964         }
15965         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15966     },
15967
15968     // private
15969     fireKey : function(e){
15970         if(e.isNavKeyPress() && !this.list.isVisible()){
15971             this.fireEvent("specialkey", this, e);
15972         }
15973     },
15974
15975     // private
15976     onResize: function(w, h)
15977     {
15978         
15979         
15980 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15981 //        
15982 //        if(typeof w != 'number'){
15983 //            // we do not handle it!?!?
15984 //            return;
15985 //        }
15986 //        var tw = this.trigger.getWidth();
15987 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15988 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15989 //        var x = w - tw;
15990 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15991 //            
15992 //        //this.trigger.setStyle('left', x+'px');
15993 //        
15994 //        if(this.list && this.listWidth === undefined){
15995 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15996 //            this.list.setWidth(lw);
15997 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15998 //        }
15999         
16000     
16001         
16002     },
16003
16004     /**
16005      * Allow or prevent the user from directly editing the field text.  If false is passed,
16006      * the user will only be able to select from the items defined in the dropdown list.  This method
16007      * is the runtime equivalent of setting the 'editable' config option at config time.
16008      * @param {Boolean} value True to allow the user to directly edit the field text
16009      */
16010     setEditable : function(value){
16011         if(value == this.editable){
16012             return;
16013         }
16014         this.editable = value;
16015         if(!value){
16016             this.inputEl().dom.setAttribute('readOnly', true);
16017             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16018             this.inputEl().addClass('x-combo-noedit');
16019         }else{
16020             this.inputEl().dom.setAttribute('readOnly', false);
16021             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16022             this.inputEl().removeClass('x-combo-noedit');
16023         }
16024     },
16025
16026     // private
16027     
16028     onBeforeLoad : function(combo,opts){
16029         if(!this.hasFocus){
16030             return;
16031         }
16032          if (!opts.add) {
16033             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16034          }
16035         this.restrictHeight();
16036         this.selectedIndex = -1;
16037     },
16038
16039     // private
16040     onLoad : function(){
16041         
16042         this.hasQuery = false;
16043         
16044         if(!this.hasFocus){
16045             return;
16046         }
16047         
16048         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16049             this.loading.hide();
16050         }
16051         
16052         if(this.store.getCount() > 0){
16053             
16054             this.expand();
16055             this.restrictHeight();
16056             if(this.lastQuery == this.allQuery){
16057                 if(this.editable && !this.tickable){
16058                     this.inputEl().dom.select();
16059                 }
16060                 
16061                 if(
16062                     !this.selectByValue(this.value, true) &&
16063                     this.autoFocus && 
16064                     (
16065                         !this.store.lastOptions ||
16066                         typeof(this.store.lastOptions.add) == 'undefined' || 
16067                         this.store.lastOptions.add != true
16068                     )
16069                 ){
16070                     this.select(0, true);
16071                 }
16072             }else{
16073                 if(this.autoFocus){
16074                     this.selectNext();
16075                 }
16076                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16077                     this.taTask.delay(this.typeAheadDelay);
16078                 }
16079             }
16080         }else{
16081             this.onEmptyResults();
16082         }
16083         
16084         //this.el.focus();
16085     },
16086     // private
16087     onLoadException : function()
16088     {
16089         this.hasQuery = false;
16090         
16091         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16092             this.loading.hide();
16093         }
16094         
16095         if(this.tickable && this.editable){
16096             return;
16097         }
16098         
16099         this.collapse();
16100         // only causes errors at present
16101         //Roo.log(this.store.reader.jsonData);
16102         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16103             // fixme
16104             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16105         //}
16106         
16107         
16108     },
16109     // private
16110     onTypeAhead : function(){
16111         if(this.store.getCount() > 0){
16112             var r = this.store.getAt(0);
16113             var newValue = r.data[this.displayField];
16114             var len = newValue.length;
16115             var selStart = this.getRawValue().length;
16116             
16117             if(selStart != len){
16118                 this.setRawValue(newValue);
16119                 this.selectText(selStart, newValue.length);
16120             }
16121         }
16122     },
16123
16124     // private
16125     onSelect : function(record, index){
16126         
16127         if(this.fireEvent('beforeselect', this, record, index) !== false){
16128         
16129             this.setFromData(index > -1 ? record.data : false);
16130             
16131             this.collapse();
16132             this.fireEvent('select', this, record, index);
16133         }
16134     },
16135
16136     /**
16137      * Returns the currently selected field value or empty string if no value is set.
16138      * @return {String} value The selected value
16139      */
16140     getValue : function()
16141     {
16142         if(Roo.isIOS && this.useNativeIOS){
16143             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16144         }
16145         
16146         if(this.multiple){
16147             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16148         }
16149         
16150         if(this.valueField){
16151             return typeof this.value != 'undefined' ? this.value : '';
16152         }else{
16153             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16154         }
16155     },
16156     
16157     getRawValue : function()
16158     {
16159         if(Roo.isIOS && this.useNativeIOS){
16160             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16161         }
16162         
16163         var v = this.inputEl().getValue();
16164         
16165         return v;
16166     },
16167
16168     /**
16169      * Clears any text/value currently set in the field
16170      */
16171     clearValue : function(){
16172         
16173         if(this.hiddenField){
16174             this.hiddenField.dom.value = '';
16175         }
16176         this.value = '';
16177         this.setRawValue('');
16178         this.lastSelectionText = '';
16179         this.lastData = false;
16180         
16181         var close = this.closeTriggerEl();
16182         
16183         if(close){
16184             close.hide();
16185         }
16186         
16187         this.validate();
16188         
16189     },
16190
16191     /**
16192      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16193      * will be displayed in the field.  If the value does not match the data value of an existing item,
16194      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16195      * Otherwise the field will be blank (although the value will still be set).
16196      * @param {String} value The value to match
16197      */
16198     setValue : function(v)
16199     {
16200         if(Roo.isIOS && this.useNativeIOS){
16201             this.setIOSValue(v);
16202             return;
16203         }
16204         
16205         if(this.multiple){
16206             this.syncValue();
16207             return;
16208         }
16209         
16210         var text = v;
16211         if(this.valueField){
16212             var r = this.findRecord(this.valueField, v);
16213             if(r){
16214                 text = r.data[this.displayField];
16215             }else if(this.valueNotFoundText !== undefined){
16216                 text = this.valueNotFoundText;
16217             }
16218         }
16219         this.lastSelectionText = text;
16220         if(this.hiddenField){
16221             this.hiddenField.dom.value = v;
16222         }
16223         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16224         this.value = v;
16225         
16226         var close = this.closeTriggerEl();
16227         
16228         if(close){
16229             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16230         }
16231         
16232         this.validate();
16233     },
16234     /**
16235      * @property {Object} the last set data for the element
16236      */
16237     
16238     lastData : false,
16239     /**
16240      * Sets the value of the field based on a object which is related to the record format for the store.
16241      * @param {Object} value the value to set as. or false on reset?
16242      */
16243     setFromData : function(o){
16244         
16245         if(this.multiple){
16246             this.addItem(o);
16247             return;
16248         }
16249             
16250         var dv = ''; // display value
16251         var vv = ''; // value value..
16252         this.lastData = o;
16253         if (this.displayField) {
16254             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16255         } else {
16256             // this is an error condition!!!
16257             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16258         }
16259         
16260         if(this.valueField){
16261             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16262         }
16263         
16264         var close = this.closeTriggerEl();
16265         
16266         if(close){
16267             if(dv.length || vv * 1 > 0){
16268                 close.show() ;
16269                 this.blockFocus=true;
16270             } else {
16271                 close.hide();
16272             }             
16273         }
16274         
16275         if(this.hiddenField){
16276             this.hiddenField.dom.value = vv;
16277             
16278             this.lastSelectionText = dv;
16279             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16280             this.value = vv;
16281             return;
16282         }
16283         // no hidden field.. - we store the value in 'value', but still display
16284         // display field!!!!
16285         this.lastSelectionText = dv;
16286         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16287         this.value = vv;
16288         
16289         
16290         
16291     },
16292     // private
16293     reset : function(){
16294         // overridden so that last data is reset..
16295         
16296         if(this.multiple){
16297             this.clearItem();
16298             return;
16299         }
16300         
16301         this.setValue(this.originalValue);
16302         //this.clearInvalid();
16303         this.lastData = false;
16304         if (this.view) {
16305             this.view.clearSelections();
16306         }
16307         
16308         this.validate();
16309     },
16310     // private
16311     findRecord : function(prop, value){
16312         var record;
16313         if(this.store.getCount() > 0){
16314             this.store.each(function(r){
16315                 if(r.data[prop] == value){
16316                     record = r;
16317                     return false;
16318                 }
16319                 return true;
16320             });
16321         }
16322         return record;
16323     },
16324     
16325     getName: function()
16326     {
16327         // returns hidden if it's set..
16328         if (!this.rendered) {return ''};
16329         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16330         
16331     },
16332     // private
16333     onViewMove : function(e, t){
16334         this.inKeyMode = false;
16335     },
16336
16337     // private
16338     onViewOver : function(e, t){
16339         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16340             return;
16341         }
16342         var item = this.view.findItemFromChild(t);
16343         
16344         if(item){
16345             var index = this.view.indexOf(item);
16346             this.select(index, false);
16347         }
16348     },
16349
16350     // private
16351     onViewClick : function(view, doFocus, el, e)
16352     {
16353         var index = this.view.getSelectedIndexes()[0];
16354         
16355         var r = this.store.getAt(index);
16356         
16357         if(this.tickable){
16358             
16359             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16360                 return;
16361             }
16362             
16363             var rm = false;
16364             var _this = this;
16365             
16366             Roo.each(this.tickItems, function(v,k){
16367                 
16368                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16369                     Roo.log(v);
16370                     _this.tickItems.splice(k, 1);
16371                     
16372                     if(typeof(e) == 'undefined' && view == false){
16373                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16374                     }
16375                     
16376                     rm = true;
16377                     return;
16378                 }
16379             });
16380             
16381             if(rm){
16382                 return;
16383             }
16384             
16385             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16386                 this.tickItems.push(r.data);
16387             }
16388             
16389             if(typeof(e) == 'undefined' && view == false){
16390                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16391             }
16392                     
16393             return;
16394         }
16395         
16396         if(r){
16397             this.onSelect(r, index);
16398         }
16399         if(doFocus !== false && !this.blockFocus){
16400             this.inputEl().focus();
16401         }
16402     },
16403
16404     // private
16405     restrictHeight : function(){
16406         //this.innerList.dom.style.height = '';
16407         //var inner = this.innerList.dom;
16408         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16409         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16410         //this.list.beginUpdate();
16411         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16412         this.list.alignTo(this.inputEl(), this.listAlign);
16413         this.list.alignTo(this.inputEl(), this.listAlign);
16414         //this.list.endUpdate();
16415     },
16416
16417     // private
16418     onEmptyResults : function(){
16419         
16420         if(this.tickable && this.editable){
16421             this.hasFocus = false;
16422             this.restrictHeight();
16423             return;
16424         }
16425         
16426         this.collapse();
16427     },
16428
16429     /**
16430      * Returns true if the dropdown list is expanded, else false.
16431      */
16432     isExpanded : function(){
16433         return this.list.isVisible();
16434     },
16435
16436     /**
16437      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16438      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16439      * @param {String} value The data value of the item to select
16440      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16441      * selected item if it is not currently in view (defaults to true)
16442      * @return {Boolean} True if the value matched an item in the list, else false
16443      */
16444     selectByValue : function(v, scrollIntoView){
16445         if(v !== undefined && v !== null){
16446             var r = this.findRecord(this.valueField || this.displayField, v);
16447             if(r){
16448                 this.select(this.store.indexOf(r), scrollIntoView);
16449                 return true;
16450             }
16451         }
16452         return false;
16453     },
16454
16455     /**
16456      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16457      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16458      * @param {Number} index The zero-based index of the list item to select
16459      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16460      * selected item if it is not currently in view (defaults to true)
16461      */
16462     select : function(index, scrollIntoView){
16463         this.selectedIndex = index;
16464         this.view.select(index);
16465         if(scrollIntoView !== false){
16466             var el = this.view.getNode(index);
16467             /*
16468              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16469              */
16470             if(el){
16471                 this.list.scrollChildIntoView(el, false);
16472             }
16473         }
16474     },
16475
16476     // private
16477     selectNext : function(){
16478         var ct = this.store.getCount();
16479         if(ct > 0){
16480             if(this.selectedIndex == -1){
16481                 this.select(0);
16482             }else if(this.selectedIndex < ct-1){
16483                 this.select(this.selectedIndex+1);
16484             }
16485         }
16486     },
16487
16488     // private
16489     selectPrev : function(){
16490         var ct = this.store.getCount();
16491         if(ct > 0){
16492             if(this.selectedIndex == -1){
16493                 this.select(0);
16494             }else if(this.selectedIndex != 0){
16495                 this.select(this.selectedIndex-1);
16496             }
16497         }
16498     },
16499
16500     // private
16501     onKeyUp : function(e){
16502         if(this.editable !== false && !e.isSpecialKey()){
16503             this.lastKey = e.getKey();
16504             this.dqTask.delay(this.queryDelay);
16505         }
16506     },
16507
16508     // private
16509     validateBlur : function(){
16510         return !this.list || !this.list.isVisible();   
16511     },
16512
16513     // private
16514     initQuery : function(){
16515         
16516         var v = this.getRawValue();
16517         
16518         if(this.tickable && this.editable){
16519             v = this.tickableInputEl().getValue();
16520         }
16521         
16522         this.doQuery(v);
16523     },
16524
16525     // private
16526     doForce : function(){
16527         if(this.inputEl().dom.value.length > 0){
16528             this.inputEl().dom.value =
16529                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16530              
16531         }
16532     },
16533
16534     /**
16535      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16536      * query allowing the query action to be canceled if needed.
16537      * @param {String} query The SQL query to execute
16538      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16539      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16540      * saved in the current store (defaults to false)
16541      */
16542     doQuery : function(q, forceAll){
16543         
16544         if(q === undefined || q === null){
16545             q = '';
16546         }
16547         var qe = {
16548             query: q,
16549             forceAll: forceAll,
16550             combo: this,
16551             cancel:false
16552         };
16553         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16554             return false;
16555         }
16556         q = qe.query;
16557         
16558         forceAll = qe.forceAll;
16559         if(forceAll === true || (q.length >= this.minChars)){
16560             
16561             this.hasQuery = true;
16562             
16563             if(this.lastQuery != q || this.alwaysQuery){
16564                 this.lastQuery = q;
16565                 if(this.mode == 'local'){
16566                     this.selectedIndex = -1;
16567                     if(forceAll){
16568                         this.store.clearFilter();
16569                     }else{
16570                         
16571                         if(this.specialFilter){
16572                             this.fireEvent('specialfilter', this);
16573                             this.onLoad();
16574                             return;
16575                         }
16576                         
16577                         this.store.filter(this.displayField, q);
16578                     }
16579                     
16580                     this.store.fireEvent("datachanged", this.store);
16581                     
16582                     this.onLoad();
16583                     
16584                     
16585                 }else{
16586                     
16587                     this.store.baseParams[this.queryParam] = q;
16588                     
16589                     var options = {params : this.getParams(q)};
16590                     
16591                     if(this.loadNext){
16592                         options.add = true;
16593                         options.params.start = this.page * this.pageSize;
16594                     }
16595                     
16596                     this.store.load(options);
16597                     
16598                     /*
16599                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16600                      *  we should expand the list on onLoad
16601                      *  so command out it
16602                      */
16603 //                    this.expand();
16604                 }
16605             }else{
16606                 this.selectedIndex = -1;
16607                 this.onLoad();   
16608             }
16609         }
16610         
16611         this.loadNext = false;
16612     },
16613     
16614     // private
16615     getParams : function(q){
16616         var p = {};
16617         //p[this.queryParam] = q;
16618         
16619         if(this.pageSize){
16620             p.start = 0;
16621             p.limit = this.pageSize;
16622         }
16623         return p;
16624     },
16625
16626     /**
16627      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16628      */
16629     collapse : function(){
16630         if(!this.isExpanded()){
16631             return;
16632         }
16633         
16634         this.list.hide();
16635         
16636         this.hasFocus = false;
16637         
16638         if(this.tickable){
16639             this.okBtn.hide();
16640             this.cancelBtn.hide();
16641             this.trigger.show();
16642             
16643             if(this.editable){
16644                 this.tickableInputEl().dom.value = '';
16645                 this.tickableInputEl().blur();
16646             }
16647             
16648         }
16649         
16650         Roo.get(document).un('mousedown', this.collapseIf, this);
16651         Roo.get(document).un('mousewheel', this.collapseIf, this);
16652         if (!this.editable) {
16653             Roo.get(document).un('keydown', this.listKeyPress, this);
16654         }
16655         this.fireEvent('collapse', this);
16656         
16657         this.validate();
16658     },
16659
16660     // private
16661     collapseIf : function(e){
16662         var in_combo  = e.within(this.el);
16663         var in_list =  e.within(this.list);
16664         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16665         
16666         if (in_combo || in_list || is_list) {
16667             //e.stopPropagation();
16668             return;
16669         }
16670         
16671         if(this.tickable){
16672             this.onTickableFooterButtonClick(e, false, false);
16673         }
16674
16675         this.collapse();
16676         
16677     },
16678
16679     /**
16680      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16681      */
16682     expand : function(){
16683        
16684         if(this.isExpanded() || !this.hasFocus){
16685             return;
16686         }
16687         
16688         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16689         this.list.setWidth(lw);
16690         
16691         Roo.log('expand');
16692         
16693         this.list.show();
16694         
16695         this.restrictHeight();
16696         
16697         if(this.tickable){
16698             
16699             this.tickItems = Roo.apply([], this.item);
16700             
16701             this.okBtn.show();
16702             this.cancelBtn.show();
16703             this.trigger.hide();
16704             
16705             if(this.editable){
16706                 this.tickableInputEl().focus();
16707             }
16708             
16709         }
16710         
16711         Roo.get(document).on('mousedown', this.collapseIf, this);
16712         Roo.get(document).on('mousewheel', this.collapseIf, this);
16713         if (!this.editable) {
16714             Roo.get(document).on('keydown', this.listKeyPress, this);
16715         }
16716         
16717         this.fireEvent('expand', this);
16718     },
16719
16720     // private
16721     // Implements the default empty TriggerField.onTriggerClick function
16722     onTriggerClick : function(e)
16723     {
16724         Roo.log('trigger click');
16725         
16726         if(this.disabled || !this.triggerList){
16727             return;
16728         }
16729         
16730         this.page = 0;
16731         this.loadNext = false;
16732         
16733         if(this.isExpanded()){
16734             this.collapse();
16735             if (!this.blockFocus) {
16736                 this.inputEl().focus();
16737             }
16738             
16739         }else {
16740             this.hasFocus = true;
16741             if(this.triggerAction == 'all') {
16742                 this.doQuery(this.allQuery, true);
16743             } else {
16744                 this.doQuery(this.getRawValue());
16745             }
16746             if (!this.blockFocus) {
16747                 this.inputEl().focus();
16748             }
16749         }
16750     },
16751     
16752     onTickableTriggerClick : function(e)
16753     {
16754         if(this.disabled){
16755             return;
16756         }
16757         
16758         this.page = 0;
16759         this.loadNext = false;
16760         this.hasFocus = true;
16761         
16762         if(this.triggerAction == 'all') {
16763             this.doQuery(this.allQuery, true);
16764         } else {
16765             this.doQuery(this.getRawValue());
16766         }
16767     },
16768     
16769     onSearchFieldClick : function(e)
16770     {
16771         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16772             this.onTickableFooterButtonClick(e, false, false);
16773             return;
16774         }
16775         
16776         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16777             return;
16778         }
16779         
16780         this.page = 0;
16781         this.loadNext = false;
16782         this.hasFocus = true;
16783         
16784         if(this.triggerAction == 'all') {
16785             this.doQuery(this.allQuery, true);
16786         } else {
16787             this.doQuery(this.getRawValue());
16788         }
16789     },
16790     
16791     listKeyPress : function(e)
16792     {
16793         //Roo.log('listkeypress');
16794         // scroll to first matching element based on key pres..
16795         if (e.isSpecialKey()) {
16796             return false;
16797         }
16798         var k = String.fromCharCode(e.getKey()).toUpperCase();
16799         //Roo.log(k);
16800         var match  = false;
16801         var csel = this.view.getSelectedNodes();
16802         var cselitem = false;
16803         if (csel.length) {
16804             var ix = this.view.indexOf(csel[0]);
16805             cselitem  = this.store.getAt(ix);
16806             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16807                 cselitem = false;
16808             }
16809             
16810         }
16811         
16812         this.store.each(function(v) { 
16813             if (cselitem) {
16814                 // start at existing selection.
16815                 if (cselitem.id == v.id) {
16816                     cselitem = false;
16817                 }
16818                 return true;
16819             }
16820                 
16821             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16822                 match = this.store.indexOf(v);
16823                 return false;
16824             }
16825             return true;
16826         }, this);
16827         
16828         if (match === false) {
16829             return true; // no more action?
16830         }
16831         // scroll to?
16832         this.view.select(match);
16833         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16834         sn.scrollIntoView(sn.dom.parentNode, false);
16835     },
16836     
16837     onViewScroll : function(e, t){
16838         
16839         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){
16840             return;
16841         }
16842         
16843         this.hasQuery = true;
16844         
16845         this.loading = this.list.select('.loading', true).first();
16846         
16847         if(this.loading === null){
16848             this.list.createChild({
16849                 tag: 'div',
16850                 cls: 'loading roo-select2-more-results roo-select2-active',
16851                 html: 'Loading more results...'
16852             });
16853             
16854             this.loading = this.list.select('.loading', true).first();
16855             
16856             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16857             
16858             this.loading.hide();
16859         }
16860         
16861         this.loading.show();
16862         
16863         var _combo = this;
16864         
16865         this.page++;
16866         this.loadNext = true;
16867         
16868         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16869         
16870         return;
16871     },
16872     
16873     addItem : function(o)
16874     {   
16875         var dv = ''; // display value
16876         
16877         if (this.displayField) {
16878             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16879         } else {
16880             // this is an error condition!!!
16881             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16882         }
16883         
16884         if(!dv.length){
16885             return;
16886         }
16887         
16888         var choice = this.choices.createChild({
16889             tag: 'li',
16890             cls: 'roo-select2-search-choice',
16891             cn: [
16892                 {
16893                     tag: 'div',
16894                     html: dv
16895                 },
16896                 {
16897                     tag: 'a',
16898                     href: '#',
16899                     cls: 'roo-select2-search-choice-close fa fa-times',
16900                     tabindex: '-1'
16901                 }
16902             ]
16903             
16904         }, this.searchField);
16905         
16906         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16907         
16908         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16909         
16910         this.item.push(o);
16911         
16912         this.lastData = o;
16913         
16914         this.syncValue();
16915         
16916         this.inputEl().dom.value = '';
16917         
16918         this.validate();
16919     },
16920     
16921     onRemoveItem : function(e, _self, o)
16922     {
16923         e.preventDefault();
16924         
16925         this.lastItem = Roo.apply([], this.item);
16926         
16927         var index = this.item.indexOf(o.data) * 1;
16928         
16929         if( index < 0){
16930             Roo.log('not this item?!');
16931             return;
16932         }
16933         
16934         this.item.splice(index, 1);
16935         o.item.remove();
16936         
16937         this.syncValue();
16938         
16939         this.fireEvent('remove', this, e);
16940         
16941         this.validate();
16942         
16943     },
16944     
16945     syncValue : function()
16946     {
16947         if(!this.item.length){
16948             this.clearValue();
16949             return;
16950         }
16951             
16952         var value = [];
16953         var _this = this;
16954         Roo.each(this.item, function(i){
16955             if(_this.valueField){
16956                 value.push(i[_this.valueField]);
16957                 return;
16958             }
16959
16960             value.push(i);
16961         });
16962
16963         this.value = value.join(',');
16964
16965         if(this.hiddenField){
16966             this.hiddenField.dom.value = this.value;
16967         }
16968         
16969         this.store.fireEvent("datachanged", this.store);
16970         
16971         this.validate();
16972     },
16973     
16974     clearItem : function()
16975     {
16976         if(!this.multiple){
16977             return;
16978         }
16979         
16980         this.item = [];
16981         
16982         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16983            c.remove();
16984         });
16985         
16986         this.syncValue();
16987         
16988         this.validate();
16989         
16990         if(this.tickable && !Roo.isTouch){
16991             this.view.refresh();
16992         }
16993     },
16994     
16995     inputEl: function ()
16996     {
16997         if(Roo.isIOS && this.useNativeIOS){
16998             return this.el.select('select.roo-ios-select', true).first();
16999         }
17000         
17001         if(Roo.isTouch && this.mobileTouchView){
17002             return this.el.select('input.form-control',true).first();
17003         }
17004         
17005         if(this.tickable){
17006             return this.searchField;
17007         }
17008         
17009         return this.el.select('input.form-control',true).first();
17010     },
17011     
17012     onTickableFooterButtonClick : function(e, btn, el)
17013     {
17014         e.preventDefault();
17015         
17016         this.lastItem = Roo.apply([], this.item);
17017         
17018         if(btn && btn.name == 'cancel'){
17019             this.tickItems = Roo.apply([], this.item);
17020             this.collapse();
17021             return;
17022         }
17023         
17024         this.clearItem();
17025         
17026         var _this = this;
17027         
17028         Roo.each(this.tickItems, function(o){
17029             _this.addItem(o);
17030         });
17031         
17032         this.collapse();
17033         
17034     },
17035     
17036     validate : function()
17037     {
17038         if(this.getVisibilityEl().hasClass('hidden')){
17039             return true;
17040         }
17041         
17042         var v = this.getRawValue();
17043         
17044         if(this.multiple){
17045             v = this.getValue();
17046         }
17047         
17048         if(this.disabled || this.allowBlank || v.length){
17049             this.markValid();
17050             return true;
17051         }
17052         
17053         this.markInvalid();
17054         return false;
17055     },
17056     
17057     tickableInputEl : function()
17058     {
17059         if(!this.tickable || !this.editable){
17060             return this.inputEl();
17061         }
17062         
17063         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17064     },
17065     
17066     
17067     getAutoCreateTouchView : function()
17068     {
17069         var id = Roo.id();
17070         
17071         var cfg = {
17072             cls: 'form-group' //input-group
17073         };
17074         
17075         var input =  {
17076             tag: 'input',
17077             id : id,
17078             type : this.inputType,
17079             cls : 'form-control x-combo-noedit',
17080             autocomplete: 'new-password',
17081             placeholder : this.placeholder || '',
17082             readonly : true
17083         };
17084         
17085         if (this.name) {
17086             input.name = this.name;
17087         }
17088         
17089         if (this.size) {
17090             input.cls += ' input-' + this.size;
17091         }
17092         
17093         if (this.disabled) {
17094             input.disabled = true;
17095         }
17096         
17097         var inputblock = {
17098             cls : 'roo-combobox-wrap',
17099             cn : [
17100                 input
17101             ]
17102         };
17103         
17104         if(this.before){
17105             inputblock.cls += ' input-group';
17106             
17107             inputblock.cn.unshift({
17108                 tag :'span',
17109                 cls : 'input-group-addon input-group-prepend input-group-text',
17110                 html : this.before
17111             });
17112         }
17113         
17114         if(this.removable && !this.multiple){
17115             inputblock.cls += ' roo-removable';
17116             
17117             inputblock.cn.push({
17118                 tag: 'button',
17119                 html : 'x',
17120                 cls : 'roo-combo-removable-btn close'
17121             });
17122         }
17123
17124         if(this.hasFeedback && !this.allowBlank){
17125             
17126             inputblock.cls += ' has-feedback';
17127             
17128             inputblock.cn.push({
17129                 tag: 'span',
17130                 cls: 'glyphicon form-control-feedback'
17131             });
17132             
17133         }
17134         
17135         if (this.after) {
17136             
17137             inputblock.cls += (this.before) ? '' : ' input-group';
17138             
17139             inputblock.cn.push({
17140                 tag :'span',
17141                 cls : 'input-group-addon input-group-append input-group-text',
17142                 html : this.after
17143             });
17144         }
17145
17146         
17147         var ibwrap = inputblock;
17148         
17149         if(this.multiple){
17150             ibwrap = {
17151                 tag: 'ul',
17152                 cls: 'roo-select2-choices',
17153                 cn:[
17154                     {
17155                         tag: 'li',
17156                         cls: 'roo-select2-search-field',
17157                         cn: [
17158
17159                             inputblock
17160                         ]
17161                     }
17162                 ]
17163             };
17164         
17165             
17166         }
17167         
17168         var combobox = {
17169             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17170             cn: [
17171                 {
17172                     tag: 'input',
17173                     type : 'hidden',
17174                     cls: 'form-hidden-field'
17175                 },
17176                 ibwrap
17177             ]
17178         };
17179         
17180         if(!this.multiple && this.showToggleBtn){
17181             
17182             var caret = {
17183                 cls: 'caret'
17184             };
17185             
17186             if (this.caret != false) {
17187                 caret = {
17188                      tag: 'i',
17189                      cls: 'fa fa-' + this.caret
17190                 };
17191                 
17192             }
17193             
17194             combobox.cn.push({
17195                 tag :'span',
17196                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17197                 cn : [
17198                     Roo.bootstrap.version == 3 ? caret : '',
17199                     {
17200                         tag: 'span',
17201                         cls: 'combobox-clear',
17202                         cn  : [
17203                             {
17204                                 tag : 'i',
17205                                 cls: 'icon-remove'
17206                             }
17207                         ]
17208                     }
17209                 ]
17210
17211             })
17212         }
17213         
17214         if(this.multiple){
17215             combobox.cls += ' roo-select2-container-multi';
17216         }
17217         
17218         var align = this.labelAlign || this.parentLabelAlign();
17219         
17220         if (align ==='left' && this.fieldLabel.length) {
17221
17222             cfg.cn = [
17223                 {
17224                    tag : 'i',
17225                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17226                    tooltip : 'This field is required'
17227                 },
17228                 {
17229                     tag: 'label',
17230                     cls : 'control-label col-form-label',
17231                     html : this.fieldLabel
17232
17233                 },
17234                 {
17235                     cls : 'roo-combobox-wrap ', 
17236                     cn: [
17237                         combobox
17238                     ]
17239                 }
17240             ];
17241             
17242             var labelCfg = cfg.cn[1];
17243             var contentCfg = cfg.cn[2];
17244             
17245
17246             if(this.indicatorpos == 'right'){
17247                 cfg.cn = [
17248                     {
17249                         tag: 'label',
17250                         'for' :  id,
17251                         cls : 'control-label col-form-label',
17252                         cn : [
17253                             {
17254                                 tag : 'span',
17255                                 html : this.fieldLabel
17256                             },
17257                             {
17258                                 tag : 'i',
17259                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17260                                 tooltip : 'This field is required'
17261                             }
17262                         ]
17263                     },
17264                     {
17265                         cls : "roo-combobox-wrap ",
17266                         cn: [
17267                             combobox
17268                         ]
17269                     }
17270
17271                 ];
17272                 
17273                 labelCfg = cfg.cn[0];
17274                 contentCfg = cfg.cn[1];
17275             }
17276             
17277            
17278             
17279             if(this.labelWidth > 12){
17280                 labelCfg.style = "width: " + this.labelWidth + 'px';
17281             }
17282            
17283             if(this.labelWidth < 13 && this.labelmd == 0){
17284                 this.labelmd = this.labelWidth;
17285             }
17286             
17287             if(this.labellg > 0){
17288                 labelCfg.cls += ' col-lg-' + this.labellg;
17289                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17290             }
17291             
17292             if(this.labelmd > 0){
17293                 labelCfg.cls += ' col-md-' + this.labelmd;
17294                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17295             }
17296             
17297             if(this.labelsm > 0){
17298                 labelCfg.cls += ' col-sm-' + this.labelsm;
17299                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17300             }
17301             
17302             if(this.labelxs > 0){
17303                 labelCfg.cls += ' col-xs-' + this.labelxs;
17304                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17305             }
17306                 
17307                 
17308         } else if ( this.fieldLabel.length) {
17309             cfg.cn = [
17310                 {
17311                    tag : 'i',
17312                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17313                    tooltip : 'This field is required'
17314                 },
17315                 {
17316                     tag: 'label',
17317                     cls : 'control-label',
17318                     html : this.fieldLabel
17319
17320                 },
17321                 {
17322                     cls : '', 
17323                     cn: [
17324                         combobox
17325                     ]
17326                 }
17327             ];
17328             
17329             if(this.indicatorpos == 'right'){
17330                 cfg.cn = [
17331                     {
17332                         tag: 'label',
17333                         cls : 'control-label',
17334                         html : this.fieldLabel,
17335                         cn : [
17336                             {
17337                                tag : 'i',
17338                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17339                                tooltip : 'This field is required'
17340                             }
17341                         ]
17342                     },
17343                     {
17344                         cls : '', 
17345                         cn: [
17346                             combobox
17347                         ]
17348                     }
17349                 ];
17350             }
17351         } else {
17352             cfg.cn = combobox;    
17353         }
17354         
17355         
17356         var settings = this;
17357         
17358         ['xs','sm','md','lg'].map(function(size){
17359             if (settings[size]) {
17360                 cfg.cls += ' col-' + size + '-' + settings[size];
17361             }
17362         });
17363         
17364         return cfg;
17365     },
17366     
17367     initTouchView : function()
17368     {
17369         this.renderTouchView();
17370         
17371         this.touchViewEl.on('scroll', function(){
17372             this.el.dom.scrollTop = 0;
17373         }, this);
17374         
17375         this.originalValue = this.getValue();
17376         
17377         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17378         
17379         this.inputEl().on("click", this.showTouchView, this);
17380         if (this.triggerEl) {
17381             this.triggerEl.on("click", this.showTouchView, this);
17382         }
17383         
17384         
17385         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17386         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17387         
17388         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17389         
17390         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17391         this.store.on('load', this.onTouchViewLoad, this);
17392         this.store.on('loadexception', this.onTouchViewLoadException, this);
17393         
17394         if(this.hiddenName){
17395             
17396             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17397             
17398             this.hiddenField.dom.value =
17399                 this.hiddenValue !== undefined ? this.hiddenValue :
17400                 this.value !== undefined ? this.value : '';
17401         
17402             this.el.dom.removeAttribute('name');
17403             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17404         }
17405         
17406         if(this.multiple){
17407             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17408             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17409         }
17410         
17411         if(this.removable && !this.multiple){
17412             var close = this.closeTriggerEl();
17413             if(close){
17414                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17415                 close.on('click', this.removeBtnClick, this, close);
17416             }
17417         }
17418         /*
17419          * fix the bug in Safari iOS8
17420          */
17421         this.inputEl().on("focus", function(e){
17422             document.activeElement.blur();
17423         }, this);
17424         
17425         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17426         
17427         return;
17428         
17429         
17430     },
17431     
17432     renderTouchView : function()
17433     {
17434         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17435         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17436         
17437         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17438         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439         
17440         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17441         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17442         this.touchViewBodyEl.setStyle('overflow', 'auto');
17443         
17444         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17445         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17446         
17447         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17448         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17449         
17450     },
17451     
17452     showTouchView : function()
17453     {
17454         if(this.disabled){
17455             return;
17456         }
17457         
17458         this.touchViewHeaderEl.hide();
17459
17460         if(this.modalTitle.length){
17461             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17462             this.touchViewHeaderEl.show();
17463         }
17464
17465         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17466         this.touchViewEl.show();
17467
17468         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17469         
17470         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17471         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17472
17473         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17474
17475         if(this.modalTitle.length){
17476             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17477         }
17478         
17479         this.touchViewBodyEl.setHeight(bodyHeight);
17480
17481         if(this.animate){
17482             var _this = this;
17483             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17484         }else{
17485             this.touchViewEl.addClass(['in','show']);
17486         }
17487         
17488         if(this._touchViewMask){
17489             Roo.get(document.body).addClass("x-body-masked");
17490             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17491             this._touchViewMask.setStyle('z-index', 10000);
17492             this._touchViewMask.addClass('show');
17493         }
17494         
17495         this.doTouchViewQuery();
17496         
17497     },
17498     
17499     hideTouchView : function()
17500     {
17501         this.touchViewEl.removeClass(['in','show']);
17502
17503         if(this.animate){
17504             var _this = this;
17505             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17506         }else{
17507             this.touchViewEl.setStyle('display', 'none');
17508         }
17509         
17510         if(this._touchViewMask){
17511             this._touchViewMask.removeClass('show');
17512             Roo.get(document.body).removeClass("x-body-masked");
17513         }
17514     },
17515     
17516     setTouchViewValue : function()
17517     {
17518         if(this.multiple){
17519             this.clearItem();
17520         
17521             var _this = this;
17522
17523             Roo.each(this.tickItems, function(o){
17524                 this.addItem(o);
17525             }, this);
17526         }
17527         
17528         this.hideTouchView();
17529     },
17530     
17531     doTouchViewQuery : function()
17532     {
17533         var qe = {
17534             query: '',
17535             forceAll: true,
17536             combo: this,
17537             cancel:false
17538         };
17539         
17540         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17541             return false;
17542         }
17543         
17544         if(!this.alwaysQuery || this.mode == 'local'){
17545             this.onTouchViewLoad();
17546             return;
17547         }
17548         
17549         this.store.load();
17550     },
17551     
17552     onTouchViewBeforeLoad : function(combo,opts)
17553     {
17554         return;
17555     },
17556
17557     // private
17558     onTouchViewLoad : function()
17559     {
17560         if(this.store.getCount() < 1){
17561             this.onTouchViewEmptyResults();
17562             return;
17563         }
17564         
17565         this.clearTouchView();
17566         
17567         var rawValue = this.getRawValue();
17568         
17569         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17570         
17571         this.tickItems = [];
17572         
17573         this.store.data.each(function(d, rowIndex){
17574             var row = this.touchViewListGroup.createChild(template);
17575             
17576             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17577                 row.addClass(d.data.cls);
17578             }
17579             
17580             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17581                 var cfg = {
17582                     data : d.data,
17583                     html : d.data[this.displayField]
17584                 };
17585                 
17586                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17587                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17588                 }
17589             }
17590             row.removeClass('selected');
17591             if(!this.multiple && this.valueField &&
17592                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17593             {
17594                 // radio buttons..
17595                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17596                 row.addClass('selected');
17597             }
17598             
17599             if(this.multiple && this.valueField &&
17600                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17601             {
17602                 
17603                 // checkboxes...
17604                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17605                 this.tickItems.push(d.data);
17606             }
17607             
17608             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17609             
17610         }, this);
17611         
17612         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17613         
17614         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17615
17616         if(this.modalTitle.length){
17617             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17618         }
17619
17620         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17621         
17622         if(this.mobile_restrict_height && listHeight < bodyHeight){
17623             this.touchViewBodyEl.setHeight(listHeight);
17624         }
17625         
17626         var _this = this;
17627         
17628         if(firstChecked && listHeight > bodyHeight){
17629             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17630         }
17631         
17632     },
17633     
17634     onTouchViewLoadException : function()
17635     {
17636         this.hideTouchView();
17637     },
17638     
17639     onTouchViewEmptyResults : function()
17640     {
17641         this.clearTouchView();
17642         
17643         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17644         
17645         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17646         
17647     },
17648     
17649     clearTouchView : function()
17650     {
17651         this.touchViewListGroup.dom.innerHTML = '';
17652     },
17653     
17654     onTouchViewClick : function(e, el, o)
17655     {
17656         e.preventDefault();
17657         
17658         var row = o.row;
17659         var rowIndex = o.rowIndex;
17660         
17661         var r = this.store.getAt(rowIndex);
17662         
17663         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17664             
17665             if(!this.multiple){
17666                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17667                     c.dom.removeAttribute('checked');
17668                 }, this);
17669
17670                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17671
17672                 this.setFromData(r.data);
17673
17674                 var close = this.closeTriggerEl();
17675
17676                 if(close){
17677                     close.show();
17678                 }
17679
17680                 this.hideTouchView();
17681
17682                 this.fireEvent('select', this, r, rowIndex);
17683
17684                 return;
17685             }
17686
17687             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17688                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17689                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17690                 return;
17691             }
17692
17693             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17694             this.addItem(r.data);
17695             this.tickItems.push(r.data);
17696         }
17697     },
17698     
17699     getAutoCreateNativeIOS : function()
17700     {
17701         var cfg = {
17702             cls: 'form-group' //input-group,
17703         };
17704         
17705         var combobox =  {
17706             tag: 'select',
17707             cls : 'roo-ios-select'
17708         };
17709         
17710         if (this.name) {
17711             combobox.name = this.name;
17712         }
17713         
17714         if (this.disabled) {
17715             combobox.disabled = true;
17716         }
17717         
17718         var settings = this;
17719         
17720         ['xs','sm','md','lg'].map(function(size){
17721             if (settings[size]) {
17722                 cfg.cls += ' col-' + size + '-' + settings[size];
17723             }
17724         });
17725         
17726         cfg.cn = combobox;
17727         
17728         return cfg;
17729         
17730     },
17731     
17732     initIOSView : function()
17733     {
17734         this.store.on('load', this.onIOSViewLoad, this);
17735         
17736         return;
17737     },
17738     
17739     onIOSViewLoad : function()
17740     {
17741         if(this.store.getCount() < 1){
17742             return;
17743         }
17744         
17745         this.clearIOSView();
17746         
17747         if(this.allowBlank) {
17748             
17749             var default_text = '-- SELECT --';
17750             
17751             if(this.placeholder.length){
17752                 default_text = this.placeholder;
17753             }
17754             
17755             if(this.emptyTitle.length){
17756                 default_text += ' - ' + this.emptyTitle + ' -';
17757             }
17758             
17759             var opt = this.inputEl().createChild({
17760                 tag: 'option',
17761                 value : 0,
17762                 html : default_text
17763             });
17764             
17765             var o = {};
17766             o[this.valueField] = 0;
17767             o[this.displayField] = default_text;
17768             
17769             this.ios_options.push({
17770                 data : o,
17771                 el : opt
17772             });
17773             
17774         }
17775         
17776         this.store.data.each(function(d, rowIndex){
17777             
17778             var html = '';
17779             
17780             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17781                 html = d.data[this.displayField];
17782             }
17783             
17784             var value = '';
17785             
17786             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17787                 value = d.data[this.valueField];
17788             }
17789             
17790             var option = {
17791                 tag: 'option',
17792                 value : value,
17793                 html : html
17794             };
17795             
17796             if(this.value == d.data[this.valueField]){
17797                 option['selected'] = true;
17798             }
17799             
17800             var opt = this.inputEl().createChild(option);
17801             
17802             this.ios_options.push({
17803                 data : d.data,
17804                 el : opt
17805             });
17806             
17807         }, this);
17808         
17809         this.inputEl().on('change', function(){
17810            this.fireEvent('select', this);
17811         }, this);
17812         
17813     },
17814     
17815     clearIOSView: function()
17816     {
17817         this.inputEl().dom.innerHTML = '';
17818         
17819         this.ios_options = [];
17820     },
17821     
17822     setIOSValue: function(v)
17823     {
17824         this.value = v;
17825         
17826         if(!this.ios_options){
17827             return;
17828         }
17829         
17830         Roo.each(this.ios_options, function(opts){
17831            
17832            opts.el.dom.removeAttribute('selected');
17833            
17834            if(opts.data[this.valueField] != v){
17835                return;
17836            }
17837            
17838            opts.el.dom.setAttribute('selected', true);
17839            
17840         }, this);
17841     }
17842
17843     /** 
17844     * @cfg {Boolean} grow 
17845     * @hide 
17846     */
17847     /** 
17848     * @cfg {Number} growMin 
17849     * @hide 
17850     */
17851     /** 
17852     * @cfg {Number} growMax 
17853     * @hide 
17854     */
17855     /**
17856      * @hide
17857      * @method autoSize
17858      */
17859 });
17860
17861 Roo.apply(Roo.bootstrap.ComboBox,  {
17862     
17863     header : {
17864         tag: 'div',
17865         cls: 'modal-header',
17866         cn: [
17867             {
17868                 tag: 'h4',
17869                 cls: 'modal-title'
17870             }
17871         ]
17872     },
17873     
17874     body : {
17875         tag: 'div',
17876         cls: 'modal-body',
17877         cn: [
17878             {
17879                 tag: 'ul',
17880                 cls: 'list-group'
17881             }
17882         ]
17883     },
17884     
17885     listItemRadio : {
17886         tag: 'li',
17887         cls: 'list-group-item',
17888         cn: [
17889             {
17890                 tag: 'span',
17891                 cls: 'roo-combobox-list-group-item-value'
17892             },
17893             {
17894                 tag: 'div',
17895                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17896                 cn: [
17897                     {
17898                         tag: 'input',
17899                         type: 'radio'
17900                     },
17901                     {
17902                         tag: 'label'
17903                     }
17904                 ]
17905             }
17906         ]
17907     },
17908     
17909     listItemCheckbox : {
17910         tag: 'li',
17911         cls: 'list-group-item',
17912         cn: [
17913             {
17914                 tag: 'span',
17915                 cls: 'roo-combobox-list-group-item-value'
17916             },
17917             {
17918                 tag: 'div',
17919                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17920                 cn: [
17921                     {
17922                         tag: 'input',
17923                         type: 'checkbox'
17924                     },
17925                     {
17926                         tag: 'label'
17927                     }
17928                 ]
17929             }
17930         ]
17931     },
17932     
17933     emptyResult : {
17934         tag: 'div',
17935         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17936     },
17937     
17938     footer : {
17939         tag: 'div',
17940         cls: 'modal-footer',
17941         cn: [
17942             {
17943                 tag: 'div',
17944                 cls: 'row',
17945                 cn: [
17946                     {
17947                         tag: 'div',
17948                         cls: 'col-xs-6 text-left',
17949                         cn: {
17950                             tag: 'button',
17951                             cls: 'btn btn-danger roo-touch-view-cancel',
17952                             html: 'Cancel'
17953                         }
17954                     },
17955                     {
17956                         tag: 'div',
17957                         cls: 'col-xs-6 text-right',
17958                         cn: {
17959                             tag: 'button',
17960                             cls: 'btn btn-success roo-touch-view-ok',
17961                             html: 'OK'
17962                         }
17963                     }
17964                 ]
17965             }
17966         ]
17967         
17968     }
17969 });
17970
17971 Roo.apply(Roo.bootstrap.ComboBox,  {
17972     
17973     touchViewTemplate : {
17974         tag: 'div',
17975         cls: 'modal fade roo-combobox-touch-view',
17976         cn: [
17977             {
17978                 tag: 'div',
17979                 cls: 'modal-dialog',
17980                 style : 'position:fixed', // we have to fix position....
17981                 cn: [
17982                     {
17983                         tag: 'div',
17984                         cls: 'modal-content',
17985                         cn: [
17986                             Roo.bootstrap.ComboBox.header,
17987                             Roo.bootstrap.ComboBox.body,
17988                             Roo.bootstrap.ComboBox.footer
17989                         ]
17990                     }
17991                 ]
17992             }
17993         ]
17994     }
17995 });/*
17996  * Based on:
17997  * Ext JS Library 1.1.1
17998  * Copyright(c) 2006-2007, Ext JS, LLC.
17999  *
18000  * Originally Released Under LGPL - original licence link has changed is not relivant.
18001  *
18002  * Fork - LGPL
18003  * <script type="text/javascript">
18004  */
18005
18006 /**
18007  * @class Roo.View
18008  * @extends Roo.util.Observable
18009  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18010  * This class also supports single and multi selection modes. <br>
18011  * Create a data model bound view:
18012  <pre><code>
18013  var store = new Roo.data.Store(...);
18014
18015  var view = new Roo.View({
18016     el : "my-element",
18017     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18018  
18019     singleSelect: true,
18020     selectedClass: "ydataview-selected",
18021     store: store
18022  });
18023
18024  // listen for node click?
18025  view.on("click", function(vw, index, node, e){
18026  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18027  });
18028
18029  // load XML data
18030  dataModel.load("foobar.xml");
18031  </code></pre>
18032  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18033  * <br><br>
18034  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18035  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18036  * 
18037  * Note: old style constructor is still suported (container, template, config)
18038  * 
18039  * @constructor
18040  * Create a new View
18041  * @param {Object} config The config object
18042  * 
18043  */
18044 Roo.View = function(config, depreciated_tpl, depreciated_config){
18045     
18046     this.parent = false;
18047     
18048     if (typeof(depreciated_tpl) == 'undefined') {
18049         // new way.. - universal constructor.
18050         Roo.apply(this, config);
18051         this.el  = Roo.get(this.el);
18052     } else {
18053         // old format..
18054         this.el  = Roo.get(config);
18055         this.tpl = depreciated_tpl;
18056         Roo.apply(this, depreciated_config);
18057     }
18058     this.wrapEl  = this.el.wrap().wrap();
18059     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18060     
18061     
18062     if(typeof(this.tpl) == "string"){
18063         this.tpl = new Roo.Template(this.tpl);
18064     } else {
18065         // support xtype ctors..
18066         this.tpl = new Roo.factory(this.tpl, Roo);
18067     }
18068     
18069     
18070     this.tpl.compile();
18071     
18072     /** @private */
18073     this.addEvents({
18074         /**
18075          * @event beforeclick
18076          * Fires before a click is processed. Returns false to cancel the default action.
18077          * @param {Roo.View} this
18078          * @param {Number} index The index of the target node
18079          * @param {HTMLElement} node The target node
18080          * @param {Roo.EventObject} e The raw event object
18081          */
18082             "beforeclick" : true,
18083         /**
18084          * @event click
18085          * Fires when a template node is clicked.
18086          * @param {Roo.View} this
18087          * @param {Number} index The index of the target node
18088          * @param {HTMLElement} node The target node
18089          * @param {Roo.EventObject} e The raw event object
18090          */
18091             "click" : true,
18092         /**
18093          * @event dblclick
18094          * Fires when a template node is double clicked.
18095          * @param {Roo.View} this
18096          * @param {Number} index The index of the target node
18097          * @param {HTMLElement} node The target node
18098          * @param {Roo.EventObject} e The raw event object
18099          */
18100             "dblclick" : true,
18101         /**
18102          * @event contextmenu
18103          * Fires when a template node is right clicked.
18104          * @param {Roo.View} this
18105          * @param {Number} index The index of the target node
18106          * @param {HTMLElement} node The target node
18107          * @param {Roo.EventObject} e The raw event object
18108          */
18109             "contextmenu" : true,
18110         /**
18111          * @event selectionchange
18112          * Fires when the selected nodes change.
18113          * @param {Roo.View} this
18114          * @param {Array} selections Array of the selected nodes
18115          */
18116             "selectionchange" : true,
18117     
18118         /**
18119          * @event beforeselect
18120          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18121          * @param {Roo.View} this
18122          * @param {HTMLElement} node The node to be selected
18123          * @param {Array} selections Array of currently selected nodes
18124          */
18125             "beforeselect" : true,
18126         /**
18127          * @event preparedata
18128          * Fires on every row to render, to allow you to change the data.
18129          * @param {Roo.View} this
18130          * @param {Object} data to be rendered (change this)
18131          */
18132           "preparedata" : true
18133           
18134           
18135         });
18136
18137
18138
18139     this.el.on({
18140         "click": this.onClick,
18141         "dblclick": this.onDblClick,
18142         "contextmenu": this.onContextMenu,
18143         scope:this
18144     });
18145
18146     this.selections = [];
18147     this.nodes = [];
18148     this.cmp = new Roo.CompositeElementLite([]);
18149     if(this.store){
18150         this.store = Roo.factory(this.store, Roo.data);
18151         this.setStore(this.store, true);
18152     }
18153     
18154     if ( this.footer && this.footer.xtype) {
18155            
18156          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18157         
18158         this.footer.dataSource = this.store;
18159         this.footer.container = fctr;
18160         this.footer = Roo.factory(this.footer, Roo);
18161         fctr.insertFirst(this.el);
18162         
18163         // this is a bit insane - as the paging toolbar seems to detach the el..
18164 //        dom.parentNode.parentNode.parentNode
18165          // they get detached?
18166     }
18167     
18168     
18169     Roo.View.superclass.constructor.call(this);
18170     
18171     
18172 };
18173
18174 Roo.extend(Roo.View, Roo.util.Observable, {
18175     
18176      /**
18177      * @cfg {Roo.data.Store} store Data store to load data from.
18178      */
18179     store : false,
18180     
18181     /**
18182      * @cfg {String|Roo.Element} el The container element.
18183      */
18184     el : '',
18185     
18186     /**
18187      * @cfg {String|Roo.Template} tpl The template used by this View 
18188      */
18189     tpl : false,
18190     /**
18191      * @cfg {String} dataName the named area of the template to use as the data area
18192      *                          Works with domtemplates roo-name="name"
18193      */
18194     dataName: false,
18195     /**
18196      * @cfg {String} selectedClass The css class to add to selected nodes
18197      */
18198     selectedClass : "x-view-selected",
18199      /**
18200      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18201      */
18202     emptyText : "",
18203     
18204     /**
18205      * @cfg {String} text to display on mask (default Loading)
18206      */
18207     mask : false,
18208     /**
18209      * @cfg {Boolean} multiSelect Allow multiple selection
18210      */
18211     multiSelect : false,
18212     /**
18213      * @cfg {Boolean} singleSelect Allow single selection
18214      */
18215     singleSelect:  false,
18216     
18217     /**
18218      * @cfg {Boolean} toggleSelect - selecting 
18219      */
18220     toggleSelect : false,
18221     
18222     /**
18223      * @cfg {Boolean} tickable - selecting 
18224      */
18225     tickable : false,
18226     
18227     /**
18228      * Returns the element this view is bound to.
18229      * @return {Roo.Element}
18230      */
18231     getEl : function(){
18232         return this.wrapEl;
18233     },
18234     
18235     
18236
18237     /**
18238      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18239      */
18240     refresh : function(){
18241         //Roo.log('refresh');
18242         var t = this.tpl;
18243         
18244         // if we are using something like 'domtemplate', then
18245         // the what gets used is:
18246         // t.applySubtemplate(NAME, data, wrapping data..)
18247         // the outer template then get' applied with
18248         //     the store 'extra data'
18249         // and the body get's added to the
18250         //      roo-name="data" node?
18251         //      <span class='roo-tpl-{name}'></span> ?????
18252         
18253         
18254         
18255         this.clearSelections();
18256         this.el.update("");
18257         var html = [];
18258         var records = this.store.getRange();
18259         if(records.length < 1) {
18260             
18261             // is this valid??  = should it render a template??
18262             
18263             this.el.update(this.emptyText);
18264             return;
18265         }
18266         var el = this.el;
18267         if (this.dataName) {
18268             this.el.update(t.apply(this.store.meta)); //????
18269             el = this.el.child('.roo-tpl-' + this.dataName);
18270         }
18271         
18272         for(var i = 0, len = records.length; i < len; i++){
18273             var data = this.prepareData(records[i].data, i, records[i]);
18274             this.fireEvent("preparedata", this, data, i, records[i]);
18275             
18276             var d = Roo.apply({}, data);
18277             
18278             if(this.tickable){
18279                 Roo.apply(d, {'roo-id' : Roo.id()});
18280                 
18281                 var _this = this;
18282             
18283                 Roo.each(this.parent.item, function(item){
18284                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18285                         return;
18286                     }
18287                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18288                 });
18289             }
18290             
18291             html[html.length] = Roo.util.Format.trim(
18292                 this.dataName ?
18293                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18294                     t.apply(d)
18295             );
18296         }
18297         
18298         
18299         
18300         el.update(html.join(""));
18301         this.nodes = el.dom.childNodes;
18302         this.updateIndexes(0);
18303     },
18304     
18305
18306     /**
18307      * Function to override to reformat the data that is sent to
18308      * the template for each node.
18309      * DEPRICATED - use the preparedata event handler.
18310      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18311      * a JSON object for an UpdateManager bound view).
18312      */
18313     prepareData : function(data, index, record)
18314     {
18315         this.fireEvent("preparedata", this, data, index, record);
18316         return data;
18317     },
18318
18319     onUpdate : function(ds, record){
18320         // Roo.log('on update');   
18321         this.clearSelections();
18322         var index = this.store.indexOf(record);
18323         var n = this.nodes[index];
18324         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18325         n.parentNode.removeChild(n);
18326         this.updateIndexes(index, index);
18327     },
18328
18329     
18330     
18331 // --------- FIXME     
18332     onAdd : function(ds, records, index)
18333     {
18334         //Roo.log(['on Add', ds, records, index] );        
18335         this.clearSelections();
18336         if(this.nodes.length == 0){
18337             this.refresh();
18338             return;
18339         }
18340         var n = this.nodes[index];
18341         for(var i = 0, len = records.length; i < len; i++){
18342             var d = this.prepareData(records[i].data, i, records[i]);
18343             if(n){
18344                 this.tpl.insertBefore(n, d);
18345             }else{
18346                 
18347                 this.tpl.append(this.el, d);
18348             }
18349         }
18350         this.updateIndexes(index);
18351     },
18352
18353     onRemove : function(ds, record, index){
18354        // Roo.log('onRemove');
18355         this.clearSelections();
18356         var el = this.dataName  ?
18357             this.el.child('.roo-tpl-' + this.dataName) :
18358             this.el; 
18359         
18360         el.dom.removeChild(this.nodes[index]);
18361         this.updateIndexes(index);
18362     },
18363
18364     /**
18365      * Refresh an individual node.
18366      * @param {Number} index
18367      */
18368     refreshNode : function(index){
18369         this.onUpdate(this.store, this.store.getAt(index));
18370     },
18371
18372     updateIndexes : function(startIndex, endIndex){
18373         var ns = this.nodes;
18374         startIndex = startIndex || 0;
18375         endIndex = endIndex || ns.length - 1;
18376         for(var i = startIndex; i <= endIndex; i++){
18377             ns[i].nodeIndex = i;
18378         }
18379     },
18380
18381     /**
18382      * Changes the data store this view uses and refresh the view.
18383      * @param {Store} store
18384      */
18385     setStore : function(store, initial){
18386         if(!initial && this.store){
18387             this.store.un("datachanged", this.refresh);
18388             this.store.un("add", this.onAdd);
18389             this.store.un("remove", this.onRemove);
18390             this.store.un("update", this.onUpdate);
18391             this.store.un("clear", this.refresh);
18392             this.store.un("beforeload", this.onBeforeLoad);
18393             this.store.un("load", this.onLoad);
18394             this.store.un("loadexception", this.onLoad);
18395         }
18396         if(store){
18397           
18398             store.on("datachanged", this.refresh, this);
18399             store.on("add", this.onAdd, this);
18400             store.on("remove", this.onRemove, this);
18401             store.on("update", this.onUpdate, this);
18402             store.on("clear", this.refresh, this);
18403             store.on("beforeload", this.onBeforeLoad, this);
18404             store.on("load", this.onLoad, this);
18405             store.on("loadexception", this.onLoad, this);
18406         }
18407         
18408         if(store){
18409             this.refresh();
18410         }
18411     },
18412     /**
18413      * onbeforeLoad - masks the loading area.
18414      *
18415      */
18416     onBeforeLoad : function(store,opts)
18417     {
18418          //Roo.log('onBeforeLoad');   
18419         if (!opts.add) {
18420             this.el.update("");
18421         }
18422         this.el.mask(this.mask ? this.mask : "Loading" ); 
18423     },
18424     onLoad : function ()
18425     {
18426         this.el.unmask();
18427     },
18428     
18429
18430     /**
18431      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18432      * @param {HTMLElement} node
18433      * @return {HTMLElement} The template node
18434      */
18435     findItemFromChild : function(node){
18436         var el = this.dataName  ?
18437             this.el.child('.roo-tpl-' + this.dataName,true) :
18438             this.el.dom; 
18439         
18440         if(!node || node.parentNode == el){
18441                     return node;
18442             }
18443             var p = node.parentNode;
18444             while(p && p != el){
18445             if(p.parentNode == el){
18446                 return p;
18447             }
18448             p = p.parentNode;
18449         }
18450             return null;
18451     },
18452
18453     /** @ignore */
18454     onClick : function(e){
18455         var item = this.findItemFromChild(e.getTarget());
18456         if(item){
18457             var index = this.indexOf(item);
18458             if(this.onItemClick(item, index, e) !== false){
18459                 this.fireEvent("click", this, index, item, e);
18460             }
18461         }else{
18462             this.clearSelections();
18463         }
18464     },
18465
18466     /** @ignore */
18467     onContextMenu : function(e){
18468         var item = this.findItemFromChild(e.getTarget());
18469         if(item){
18470             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18471         }
18472     },
18473
18474     /** @ignore */
18475     onDblClick : function(e){
18476         var item = this.findItemFromChild(e.getTarget());
18477         if(item){
18478             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18479         }
18480     },
18481
18482     onItemClick : function(item, index, e)
18483     {
18484         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18485             return false;
18486         }
18487         if (this.toggleSelect) {
18488             var m = this.isSelected(item) ? 'unselect' : 'select';
18489             //Roo.log(m);
18490             var _t = this;
18491             _t[m](item, true, false);
18492             return true;
18493         }
18494         if(this.multiSelect || this.singleSelect){
18495             if(this.multiSelect && e.shiftKey && this.lastSelection){
18496                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18497             }else{
18498                 this.select(item, this.multiSelect && e.ctrlKey);
18499                 this.lastSelection = item;
18500             }
18501             
18502             if(!this.tickable){
18503                 e.preventDefault();
18504             }
18505             
18506         }
18507         return true;
18508     },
18509
18510     /**
18511      * Get the number of selected nodes.
18512      * @return {Number}
18513      */
18514     getSelectionCount : function(){
18515         return this.selections.length;
18516     },
18517
18518     /**
18519      * Get the currently selected nodes.
18520      * @return {Array} An array of HTMLElements
18521      */
18522     getSelectedNodes : function(){
18523         return this.selections;
18524     },
18525
18526     /**
18527      * Get the indexes of the selected nodes.
18528      * @return {Array}
18529      */
18530     getSelectedIndexes : function(){
18531         var indexes = [], s = this.selections;
18532         for(var i = 0, len = s.length; i < len; i++){
18533             indexes.push(s[i].nodeIndex);
18534         }
18535         return indexes;
18536     },
18537
18538     /**
18539      * Clear all selections
18540      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18541      */
18542     clearSelections : function(suppressEvent){
18543         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18544             this.cmp.elements = this.selections;
18545             this.cmp.removeClass(this.selectedClass);
18546             this.selections = [];
18547             if(!suppressEvent){
18548                 this.fireEvent("selectionchange", this, this.selections);
18549             }
18550         }
18551     },
18552
18553     /**
18554      * Returns true if the passed node is selected
18555      * @param {HTMLElement/Number} node The node or node index
18556      * @return {Boolean}
18557      */
18558     isSelected : function(node){
18559         var s = this.selections;
18560         if(s.length < 1){
18561             return false;
18562         }
18563         node = this.getNode(node);
18564         return s.indexOf(node) !== -1;
18565     },
18566
18567     /**
18568      * Selects nodes.
18569      * @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
18570      * @param {Boolean} keepExisting (optional) true to keep existing selections
18571      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18572      */
18573     select : function(nodeInfo, keepExisting, suppressEvent){
18574         if(nodeInfo instanceof Array){
18575             if(!keepExisting){
18576                 this.clearSelections(true);
18577             }
18578             for(var i = 0, len = nodeInfo.length; i < len; i++){
18579                 this.select(nodeInfo[i], true, true);
18580             }
18581             return;
18582         } 
18583         var node = this.getNode(nodeInfo);
18584         if(!node || this.isSelected(node)){
18585             return; // already selected.
18586         }
18587         if(!keepExisting){
18588             this.clearSelections(true);
18589         }
18590         
18591         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18592             Roo.fly(node).addClass(this.selectedClass);
18593             this.selections.push(node);
18594             if(!suppressEvent){
18595                 this.fireEvent("selectionchange", this, this.selections);
18596             }
18597         }
18598         
18599         
18600     },
18601       /**
18602      * Unselects nodes.
18603      * @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
18604      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18605      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18606      */
18607     unselect : function(nodeInfo, keepExisting, suppressEvent)
18608     {
18609         if(nodeInfo instanceof Array){
18610             Roo.each(this.selections, function(s) {
18611                 this.unselect(s, nodeInfo);
18612             }, this);
18613             return;
18614         }
18615         var node = this.getNode(nodeInfo);
18616         if(!node || !this.isSelected(node)){
18617             //Roo.log("not selected");
18618             return; // not selected.
18619         }
18620         // fireevent???
18621         var ns = [];
18622         Roo.each(this.selections, function(s) {
18623             if (s == node ) {
18624                 Roo.fly(node).removeClass(this.selectedClass);
18625
18626                 return;
18627             }
18628             ns.push(s);
18629         },this);
18630         
18631         this.selections= ns;
18632         this.fireEvent("selectionchange", this, this.selections);
18633     },
18634
18635     /**
18636      * Gets a template node.
18637      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18638      * @return {HTMLElement} The node or null if it wasn't found
18639      */
18640     getNode : function(nodeInfo){
18641         if(typeof nodeInfo == "string"){
18642             return document.getElementById(nodeInfo);
18643         }else if(typeof nodeInfo == "number"){
18644             return this.nodes[nodeInfo];
18645         }
18646         return nodeInfo;
18647     },
18648
18649     /**
18650      * Gets a range template nodes.
18651      * @param {Number} startIndex
18652      * @param {Number} endIndex
18653      * @return {Array} An array of nodes
18654      */
18655     getNodes : function(start, end){
18656         var ns = this.nodes;
18657         start = start || 0;
18658         end = typeof end == "undefined" ? ns.length - 1 : end;
18659         var nodes = [];
18660         if(start <= end){
18661             for(var i = start; i <= end; i++){
18662                 nodes.push(ns[i]);
18663             }
18664         } else{
18665             for(var i = start; i >= end; i--){
18666                 nodes.push(ns[i]);
18667             }
18668         }
18669         return nodes;
18670     },
18671
18672     /**
18673      * Finds the index of the passed node
18674      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18675      * @return {Number} The index of the node or -1
18676      */
18677     indexOf : function(node){
18678         node = this.getNode(node);
18679         if(typeof node.nodeIndex == "number"){
18680             return node.nodeIndex;
18681         }
18682         var ns = this.nodes;
18683         for(var i = 0, len = ns.length; i < len; i++){
18684             if(ns[i] == node){
18685                 return i;
18686             }
18687         }
18688         return -1;
18689     }
18690 });
18691 /*
18692  * - LGPL
18693  *
18694  * based on jquery fullcalendar
18695  * 
18696  */
18697
18698 Roo.bootstrap = Roo.bootstrap || {};
18699 /**
18700  * @class Roo.bootstrap.Calendar
18701  * @extends Roo.bootstrap.Component
18702  * Bootstrap Calendar class
18703  * @cfg {Boolean} loadMask (true|false) default false
18704  * @cfg {Object} header generate the user specific header of the calendar, default false
18705
18706  * @constructor
18707  * Create a new Container
18708  * @param {Object} config The config object
18709  */
18710
18711
18712
18713 Roo.bootstrap.Calendar = function(config){
18714     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18715      this.addEvents({
18716         /**
18717              * @event select
18718              * Fires when a date is selected
18719              * @param {DatePicker} this
18720              * @param {Date} date The selected date
18721              */
18722         'select': true,
18723         /**
18724              * @event monthchange
18725              * Fires when the displayed month changes 
18726              * @param {DatePicker} this
18727              * @param {Date} date The selected month
18728              */
18729         'monthchange': true,
18730         /**
18731              * @event evententer
18732              * Fires when mouse over an event
18733              * @param {Calendar} this
18734              * @param {event} Event
18735              */
18736         'evententer': true,
18737         /**
18738              * @event eventleave
18739              * Fires when the mouse leaves an
18740              * @param {Calendar} this
18741              * @param {event}
18742              */
18743         'eventleave': true,
18744         /**
18745              * @event eventclick
18746              * Fires when the mouse click an
18747              * @param {Calendar} this
18748              * @param {event}
18749              */
18750         'eventclick': true
18751         
18752     });
18753
18754 };
18755
18756 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18757     
18758      /**
18759      * @cfg {Number} startDay
18760      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18761      */
18762     startDay : 0,
18763     
18764     loadMask : false,
18765     
18766     header : false,
18767       
18768     getAutoCreate : function(){
18769         
18770         
18771         var fc_button = function(name, corner, style, content ) {
18772             return Roo.apply({},{
18773                 tag : 'span',
18774                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18775                          (corner.length ?
18776                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18777                             ''
18778                         ),
18779                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18780                 unselectable: 'on'
18781             });
18782         };
18783         
18784         var header = {};
18785         
18786         if(!this.header){
18787             header = {
18788                 tag : 'table',
18789                 cls : 'fc-header',
18790                 style : 'width:100%',
18791                 cn : [
18792                     {
18793                         tag: 'tr',
18794                         cn : [
18795                             {
18796                                 tag : 'td',
18797                                 cls : 'fc-header-left',
18798                                 cn : [
18799                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18800                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18801                                     { tag: 'span', cls: 'fc-header-space' },
18802                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18803
18804
18805                                 ]
18806                             },
18807
18808                             {
18809                                 tag : 'td',
18810                                 cls : 'fc-header-center',
18811                                 cn : [
18812                                     {
18813                                         tag: 'span',
18814                                         cls: 'fc-header-title',
18815                                         cn : {
18816                                             tag: 'H2',
18817                                             html : 'month / year'
18818                                         }
18819                                     }
18820
18821                                 ]
18822                             },
18823                             {
18824                                 tag : 'td',
18825                                 cls : 'fc-header-right',
18826                                 cn : [
18827                               /*      fc_button('month', 'left', '', 'month' ),
18828                                     fc_button('week', '', '', 'week' ),
18829                                     fc_button('day', 'right', '', 'day' )
18830                                 */    
18831
18832                                 ]
18833                             }
18834
18835                         ]
18836                     }
18837                 ]
18838             };
18839         }
18840         
18841         header = this.header;
18842         
18843        
18844         var cal_heads = function() {
18845             var ret = [];
18846             // fixme - handle this.
18847             
18848             for (var i =0; i < Date.dayNames.length; i++) {
18849                 var d = Date.dayNames[i];
18850                 ret.push({
18851                     tag: 'th',
18852                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18853                     html : d.substring(0,3)
18854                 });
18855                 
18856             }
18857             ret[0].cls += ' fc-first';
18858             ret[6].cls += ' fc-last';
18859             return ret;
18860         };
18861         var cal_cell = function(n) {
18862             return  {
18863                 tag: 'td',
18864                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18865                 cn : [
18866                     {
18867                         cn : [
18868                             {
18869                                 cls: 'fc-day-number',
18870                                 html: 'D'
18871                             },
18872                             {
18873                                 cls: 'fc-day-content',
18874                              
18875                                 cn : [
18876                                      {
18877                                         style: 'position: relative;' // height: 17px;
18878                                     }
18879                                 ]
18880                             }
18881                             
18882                             
18883                         ]
18884                     }
18885                 ]
18886                 
18887             }
18888         };
18889         var cal_rows = function() {
18890             
18891             var ret = [];
18892             for (var r = 0; r < 6; r++) {
18893                 var row= {
18894                     tag : 'tr',
18895                     cls : 'fc-week',
18896                     cn : []
18897                 };
18898                 
18899                 for (var i =0; i < Date.dayNames.length; i++) {
18900                     var d = Date.dayNames[i];
18901                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18902
18903                 }
18904                 row.cn[0].cls+=' fc-first';
18905                 row.cn[0].cn[0].style = 'min-height:90px';
18906                 row.cn[6].cls+=' fc-last';
18907                 ret.push(row);
18908                 
18909             }
18910             ret[0].cls += ' fc-first';
18911             ret[4].cls += ' fc-prev-last';
18912             ret[5].cls += ' fc-last';
18913             return ret;
18914             
18915         };
18916         
18917         var cal_table = {
18918             tag: 'table',
18919             cls: 'fc-border-separate',
18920             style : 'width:100%',
18921             cellspacing  : 0,
18922             cn : [
18923                 { 
18924                     tag: 'thead',
18925                     cn : [
18926                         { 
18927                             tag: 'tr',
18928                             cls : 'fc-first fc-last',
18929                             cn : cal_heads()
18930                         }
18931                     ]
18932                 },
18933                 { 
18934                     tag: 'tbody',
18935                     cn : cal_rows()
18936                 }
18937                   
18938             ]
18939         };
18940          
18941          var cfg = {
18942             cls : 'fc fc-ltr',
18943             cn : [
18944                 header,
18945                 {
18946                     cls : 'fc-content',
18947                     style : "position: relative;",
18948                     cn : [
18949                         {
18950                             cls : 'fc-view fc-view-month fc-grid',
18951                             style : 'position: relative',
18952                             unselectable : 'on',
18953                             cn : [
18954                                 {
18955                                     cls : 'fc-event-container',
18956                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18957                                 },
18958                                 cal_table
18959                             ]
18960                         }
18961                     ]
18962     
18963                 }
18964            ] 
18965             
18966         };
18967         
18968          
18969         
18970         return cfg;
18971     },
18972     
18973     
18974     initEvents : function()
18975     {
18976         if(!this.store){
18977             throw "can not find store for calendar";
18978         }
18979         
18980         var mark = {
18981             tag: "div",
18982             cls:"x-dlg-mask",
18983             style: "text-align:center",
18984             cn: [
18985                 {
18986                     tag: "div",
18987                     style: "background-color:white;width:50%;margin:250 auto",
18988                     cn: [
18989                         {
18990                             tag: "img",
18991                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18992                         },
18993                         {
18994                             tag: "span",
18995                             html: "Loading"
18996                         }
18997                         
18998                     ]
18999                 }
19000             ]
19001         };
19002         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19003         
19004         var size = this.el.select('.fc-content', true).first().getSize();
19005         this.maskEl.setSize(size.width, size.height);
19006         this.maskEl.enableDisplayMode("block");
19007         if(!this.loadMask){
19008             this.maskEl.hide();
19009         }
19010         
19011         this.store = Roo.factory(this.store, Roo.data);
19012         this.store.on('load', this.onLoad, this);
19013         this.store.on('beforeload', this.onBeforeLoad, this);
19014         
19015         this.resize();
19016         
19017         this.cells = this.el.select('.fc-day',true);
19018         //Roo.log(this.cells);
19019         this.textNodes = this.el.query('.fc-day-number');
19020         this.cells.addClassOnOver('fc-state-hover');
19021         
19022         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19023         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19024         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19025         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19026         
19027         this.on('monthchange', this.onMonthChange, this);
19028         
19029         this.update(new Date().clearTime());
19030     },
19031     
19032     resize : function() {
19033         var sz  = this.el.getSize();
19034         
19035         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19036         this.el.select('.fc-day-content div',true).setHeight(34);
19037     },
19038     
19039     
19040     // private
19041     showPrevMonth : function(e){
19042         this.update(this.activeDate.add("mo", -1));
19043     },
19044     showToday : function(e){
19045         this.update(new Date().clearTime());
19046     },
19047     // private
19048     showNextMonth : function(e){
19049         this.update(this.activeDate.add("mo", 1));
19050     },
19051
19052     // private
19053     showPrevYear : function(){
19054         this.update(this.activeDate.add("y", -1));
19055     },
19056
19057     // private
19058     showNextYear : function(){
19059         this.update(this.activeDate.add("y", 1));
19060     },
19061
19062     
19063    // private
19064     update : function(date)
19065     {
19066         var vd = this.activeDate;
19067         this.activeDate = date;
19068 //        if(vd && this.el){
19069 //            var t = date.getTime();
19070 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19071 //                Roo.log('using add remove');
19072 //                
19073 //                this.fireEvent('monthchange', this, date);
19074 //                
19075 //                this.cells.removeClass("fc-state-highlight");
19076 //                this.cells.each(function(c){
19077 //                   if(c.dateValue == t){
19078 //                       c.addClass("fc-state-highlight");
19079 //                       setTimeout(function(){
19080 //                            try{c.dom.firstChild.focus();}catch(e){}
19081 //                       }, 50);
19082 //                       return false;
19083 //                   }
19084 //                   return true;
19085 //                });
19086 //                return;
19087 //            }
19088 //        }
19089         
19090         var days = date.getDaysInMonth();
19091         
19092         var firstOfMonth = date.getFirstDateOfMonth();
19093         var startingPos = firstOfMonth.getDay()-this.startDay;
19094         
19095         if(startingPos < this.startDay){
19096             startingPos += 7;
19097         }
19098         
19099         var pm = date.add(Date.MONTH, -1);
19100         var prevStart = pm.getDaysInMonth()-startingPos;
19101 //        
19102         this.cells = this.el.select('.fc-day',true);
19103         this.textNodes = this.el.query('.fc-day-number');
19104         this.cells.addClassOnOver('fc-state-hover');
19105         
19106         var cells = this.cells.elements;
19107         var textEls = this.textNodes;
19108         
19109         Roo.each(cells, function(cell){
19110             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19111         });
19112         
19113         days += startingPos;
19114
19115         // convert everything to numbers so it's fast
19116         var day = 86400000;
19117         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19118         //Roo.log(d);
19119         //Roo.log(pm);
19120         //Roo.log(prevStart);
19121         
19122         var today = new Date().clearTime().getTime();
19123         var sel = date.clearTime().getTime();
19124         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19125         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19126         var ddMatch = this.disabledDatesRE;
19127         var ddText = this.disabledDatesText;
19128         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19129         var ddaysText = this.disabledDaysText;
19130         var format = this.format;
19131         
19132         var setCellClass = function(cal, cell){
19133             cell.row = 0;
19134             cell.events = [];
19135             cell.more = [];
19136             //Roo.log('set Cell Class');
19137             cell.title = "";
19138             var t = d.getTime();
19139             
19140             //Roo.log(d);
19141             
19142             cell.dateValue = t;
19143             if(t == today){
19144                 cell.className += " fc-today";
19145                 cell.className += " fc-state-highlight";
19146                 cell.title = cal.todayText;
19147             }
19148             if(t == sel){
19149                 // disable highlight in other month..
19150                 //cell.className += " fc-state-highlight";
19151                 
19152             }
19153             // disabling
19154             if(t < min) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.minText;
19157                 return;
19158             }
19159             if(t > max) {
19160                 cell.className = " fc-state-disabled";
19161                 cell.title = cal.maxText;
19162                 return;
19163             }
19164             if(ddays){
19165                 if(ddays.indexOf(d.getDay()) != -1){
19166                     cell.title = ddaysText;
19167                     cell.className = " fc-state-disabled";
19168                 }
19169             }
19170             if(ddMatch && format){
19171                 var fvalue = d.dateFormat(format);
19172                 if(ddMatch.test(fvalue)){
19173                     cell.title = ddText.replace("%0", fvalue);
19174                     cell.className = " fc-state-disabled";
19175                 }
19176             }
19177             
19178             if (!cell.initialClassName) {
19179                 cell.initialClassName = cell.dom.className;
19180             }
19181             
19182             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19183         };
19184
19185         var i = 0;
19186         
19187         for(; i < startingPos; i++) {
19188             textEls[i].innerHTML = (++prevStart);
19189             d.setDate(d.getDate()+1);
19190             
19191             cells[i].className = "fc-past fc-other-month";
19192             setCellClass(this, cells[i]);
19193         }
19194         
19195         var intDay = 0;
19196         
19197         for(; i < days; i++){
19198             intDay = i - startingPos + 1;
19199             textEls[i].innerHTML = (intDay);
19200             d.setDate(d.getDate()+1);
19201             
19202             cells[i].className = ''; // "x-date-active";
19203             setCellClass(this, cells[i]);
19204         }
19205         var extraDays = 0;
19206         
19207         for(; i < 42; i++) {
19208             textEls[i].innerHTML = (++extraDays);
19209             d.setDate(d.getDate()+1);
19210             
19211             cells[i].className = "fc-future fc-other-month";
19212             setCellClass(this, cells[i]);
19213         }
19214         
19215         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19216         
19217         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19218         
19219         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19220         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19221         
19222         if(totalRows != 6){
19223             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19224             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19225         }
19226         
19227         this.fireEvent('monthchange', this, date);
19228         
19229         
19230         /*
19231         if(!this.internalRender){
19232             var main = this.el.dom.firstChild;
19233             var w = main.offsetWidth;
19234             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19235             Roo.fly(main).setWidth(w);
19236             this.internalRender = true;
19237             // opera does not respect the auto grow header center column
19238             // then, after it gets a width opera refuses to recalculate
19239             // without a second pass
19240             if(Roo.isOpera && !this.secondPass){
19241                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19242                 this.secondPass = true;
19243                 this.update.defer(10, this, [date]);
19244             }
19245         }
19246         */
19247         
19248     },
19249     
19250     findCell : function(dt) {
19251         dt = dt.clearTime().getTime();
19252         var ret = false;
19253         this.cells.each(function(c){
19254             //Roo.log("check " +c.dateValue + '?=' + dt);
19255             if(c.dateValue == dt){
19256                 ret = c;
19257                 return false;
19258             }
19259             return true;
19260         });
19261         
19262         return ret;
19263     },
19264     
19265     findCells : function(ev) {
19266         var s = ev.start.clone().clearTime().getTime();
19267        // Roo.log(s);
19268         var e= ev.end.clone().clearTime().getTime();
19269        // Roo.log(e);
19270         var ret = [];
19271         this.cells.each(function(c){
19272              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19273             
19274             if(c.dateValue > e){
19275                 return ;
19276             }
19277             if(c.dateValue < s){
19278                 return ;
19279             }
19280             ret.push(c);
19281         });
19282         
19283         return ret;    
19284     },
19285     
19286 //    findBestRow: function(cells)
19287 //    {
19288 //        var ret = 0;
19289 //        
19290 //        for (var i =0 ; i < cells.length;i++) {
19291 //            ret  = Math.max(cells[i].rows || 0,ret);
19292 //        }
19293 //        return ret;
19294 //        
19295 //    },
19296     
19297     
19298     addItem : function(ev)
19299     {
19300         // look for vertical location slot in
19301         var cells = this.findCells(ev);
19302         
19303 //        ev.row = this.findBestRow(cells);
19304         
19305         // work out the location.
19306         
19307         var crow = false;
19308         var rows = [];
19309         for(var i =0; i < cells.length; i++) {
19310             
19311             cells[i].row = cells[0].row;
19312             
19313             if(i == 0){
19314                 cells[i].row = cells[i].row + 1;
19315             }
19316             
19317             if (!crow) {
19318                 crow = {
19319                     start : cells[i],
19320                     end :  cells[i]
19321                 };
19322                 continue;
19323             }
19324             if (crow.start.getY() == cells[i].getY()) {
19325                 // on same row.
19326                 crow.end = cells[i];
19327                 continue;
19328             }
19329             // different row.
19330             rows.push(crow);
19331             crow = {
19332                 start: cells[i],
19333                 end : cells[i]
19334             };
19335             
19336         }
19337         
19338         rows.push(crow);
19339         ev.els = [];
19340         ev.rows = rows;
19341         ev.cells = cells;
19342         
19343         cells[0].events.push(ev);
19344         
19345         this.calevents.push(ev);
19346     },
19347     
19348     clearEvents: function() {
19349         
19350         if(!this.calevents){
19351             return;
19352         }
19353         
19354         Roo.each(this.cells.elements, function(c){
19355             c.row = 0;
19356             c.events = [];
19357             c.more = [];
19358         });
19359         
19360         Roo.each(this.calevents, function(e) {
19361             Roo.each(e.els, function(el) {
19362                 el.un('mouseenter' ,this.onEventEnter, this);
19363                 el.un('mouseleave' ,this.onEventLeave, this);
19364                 el.remove();
19365             },this);
19366         },this);
19367         
19368         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19369             e.remove();
19370         });
19371         
19372     },
19373     
19374     renderEvents: function()
19375     {   
19376         var _this = this;
19377         
19378         this.cells.each(function(c) {
19379             
19380             if(c.row < 5){
19381                 return;
19382             }
19383             
19384             var ev = c.events;
19385             
19386             var r = 4;
19387             if(c.row != c.events.length){
19388                 r = 4 - (4 - (c.row - c.events.length));
19389             }
19390             
19391             c.events = ev.slice(0, r);
19392             c.more = ev.slice(r);
19393             
19394             if(c.more.length && c.more.length == 1){
19395                 c.events.push(c.more.pop());
19396             }
19397             
19398             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19399             
19400         });
19401             
19402         this.cells.each(function(c) {
19403             
19404             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19405             
19406             
19407             for (var e = 0; e < c.events.length; e++){
19408                 var ev = c.events[e];
19409                 var rows = ev.rows;
19410                 
19411                 for(var i = 0; i < rows.length; i++) {
19412                 
19413                     // how many rows should it span..
19414
19415                     var  cfg = {
19416                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19417                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19418
19419                         unselectable : "on",
19420                         cn : [
19421                             {
19422                                 cls: 'fc-event-inner',
19423                                 cn : [
19424     //                                {
19425     //                                  tag:'span',
19426     //                                  cls: 'fc-event-time',
19427     //                                  html : cells.length > 1 ? '' : ev.time
19428     //                                },
19429                                     {
19430                                       tag:'span',
19431                                       cls: 'fc-event-title',
19432                                       html : String.format('{0}', ev.title)
19433                                     }
19434
19435
19436                                 ]
19437                             },
19438                             {
19439                                 cls: 'ui-resizable-handle ui-resizable-e',
19440                                 html : '&nbsp;&nbsp;&nbsp'
19441                             }
19442
19443                         ]
19444                     };
19445
19446                     if (i == 0) {
19447                         cfg.cls += ' fc-event-start';
19448                     }
19449                     if ((i+1) == rows.length) {
19450                         cfg.cls += ' fc-event-end';
19451                     }
19452
19453                     var ctr = _this.el.select('.fc-event-container',true).first();
19454                     var cg = ctr.createChild(cfg);
19455
19456                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19457                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19458
19459                     var r = (c.more.length) ? 1 : 0;
19460                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19461                     cg.setWidth(ebox.right - sbox.x -2);
19462
19463                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19464                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19465                     cg.on('click', _this.onEventClick, _this, ev);
19466
19467                     ev.els.push(cg);
19468                     
19469                 }
19470                 
19471             }
19472             
19473             
19474             if(c.more.length){
19475                 var  cfg = {
19476                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19477                     style : 'position: absolute',
19478                     unselectable : "on",
19479                     cn : [
19480                         {
19481                             cls: 'fc-event-inner',
19482                             cn : [
19483                                 {
19484                                   tag:'span',
19485                                   cls: 'fc-event-title',
19486                                   html : 'More'
19487                                 }
19488
19489
19490                             ]
19491                         },
19492                         {
19493                             cls: 'ui-resizable-handle ui-resizable-e',
19494                             html : '&nbsp;&nbsp;&nbsp'
19495                         }
19496
19497                     ]
19498                 };
19499
19500                 var ctr = _this.el.select('.fc-event-container',true).first();
19501                 var cg = ctr.createChild(cfg);
19502
19503                 var sbox = c.select('.fc-day-content',true).first().getBox();
19504                 var ebox = c.select('.fc-day-content',true).first().getBox();
19505                 //Roo.log(cg);
19506                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19507                 cg.setWidth(ebox.right - sbox.x -2);
19508
19509                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19510                 
19511             }
19512             
19513         });
19514         
19515         
19516         
19517     },
19518     
19519     onEventEnter: function (e, el,event,d) {
19520         this.fireEvent('evententer', this, el, event);
19521     },
19522     
19523     onEventLeave: function (e, el,event,d) {
19524         this.fireEvent('eventleave', this, el, event);
19525     },
19526     
19527     onEventClick: function (e, el,event,d) {
19528         this.fireEvent('eventclick', this, el, event);
19529     },
19530     
19531     onMonthChange: function () {
19532         this.store.load();
19533     },
19534     
19535     onMoreEventClick: function(e, el, more)
19536     {
19537         var _this = this;
19538         
19539         this.calpopover.placement = 'right';
19540         this.calpopover.setTitle('More');
19541         
19542         this.calpopover.setContent('');
19543         
19544         var ctr = this.calpopover.el.select('.popover-content', true).first();
19545         
19546         Roo.each(more, function(m){
19547             var cfg = {
19548                 cls : 'fc-event-hori fc-event-draggable',
19549                 html : m.title
19550             };
19551             var cg = ctr.createChild(cfg);
19552             
19553             cg.on('click', _this.onEventClick, _this, m);
19554         });
19555         
19556         this.calpopover.show(el);
19557         
19558         
19559     },
19560     
19561     onLoad: function () 
19562     {   
19563         this.calevents = [];
19564         var cal = this;
19565         
19566         if(this.store.getCount() > 0){
19567             this.store.data.each(function(d){
19568                cal.addItem({
19569                     id : d.data.id,
19570                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19571                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19572                     time : d.data.start_time,
19573                     title : d.data.title,
19574                     description : d.data.description,
19575                     venue : d.data.venue
19576                 });
19577             });
19578         }
19579         
19580         this.renderEvents();
19581         
19582         if(this.calevents.length && this.loadMask){
19583             this.maskEl.hide();
19584         }
19585     },
19586     
19587     onBeforeLoad: function()
19588     {
19589         this.clearEvents();
19590         if(this.loadMask){
19591             this.maskEl.show();
19592         }
19593     }
19594 });
19595
19596  
19597  /*
19598  * - LGPL
19599  *
19600  * element
19601  * 
19602  */
19603
19604 /**
19605  * @class Roo.bootstrap.Popover
19606  * @extends Roo.bootstrap.Component
19607  * Bootstrap Popover class
19608  * @cfg {String} html contents of the popover   (or false to use children..)
19609  * @cfg {String} title of popover (or false to hide)
19610  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19611  * @cfg {String} trigger click || hover (or false to trigger manually)
19612  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19613  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19614  *      - if false and it has a 'parent' then it will be automatically added to that element
19615  *      - if string - Roo.get  will be called 
19616  * @cfg {Number} delay - delay before showing
19617  
19618  * @constructor
19619  * Create a new Popover
19620  * @param {Object} config The config object
19621  */
19622
19623 Roo.bootstrap.Popover = function(config){
19624     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19625     
19626     this.addEvents({
19627         // raw events
19628          /**
19629          * @event show
19630          * After the popover show
19631          * 
19632          * @param {Roo.bootstrap.Popover} this
19633          */
19634         "show" : true,
19635         /**
19636          * @event hide
19637          * After the popover hide
19638          * 
19639          * @param {Roo.bootstrap.Popover} this
19640          */
19641         "hide" : true
19642     });
19643 };
19644
19645 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19646     
19647     title: false,
19648     html: false,
19649     
19650     placement : 'right',
19651     trigger : 'hover', // hover
19652     modal : false,
19653     delay : 0,
19654     
19655     over: false,
19656     
19657     can_build_overlaid : false,
19658     
19659     maskEl : false, // the mask element
19660     headerEl : false,
19661     contentEl : false,
19662     alignEl : false, // when show is called with an element - this get's stored.
19663     
19664     getChildContainer : function()
19665     {
19666         return this.contentEl;
19667         
19668     },
19669     getPopoverHeader : function()
19670     {
19671         this.title = true; // flag not to hide it..
19672         this.headerEl.addClass('p-0');
19673         return this.headerEl
19674     },
19675     
19676     
19677     getAutoCreate : function(){
19678          
19679         var cfg = {
19680            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19681            style: 'display:block',
19682            cn : [
19683                 {
19684                     cls : 'arrow'
19685                 },
19686                 {
19687                     cls : 'popover-inner ',
19688                     cn : [
19689                         {
19690                             tag: 'h3',
19691                             cls: 'popover-title popover-header',
19692                             html : this.title === false ? '' : this.title
19693                         },
19694                         {
19695                             cls : 'popover-content popover-body '  + (this.cls || ''),
19696                             html : this.html || ''
19697                         }
19698                     ]
19699                     
19700                 }
19701            ]
19702         };
19703         
19704         return cfg;
19705     },
19706     /**
19707      * @param {string} the title
19708      */
19709     setTitle: function(str)
19710     {
19711         this.title = str;
19712         if (this.el) {
19713             this.headerEl.dom.innerHTML = str;
19714         }
19715         
19716     },
19717     /**
19718      * @param {string} the body content
19719      */
19720     setContent: function(str)
19721     {
19722         this.html = str;
19723         if (this.contentEl) {
19724             this.contentEl.dom.innerHTML = str;
19725         }
19726         
19727     },
19728     // as it get's added to the bottom of the page.
19729     onRender : function(ct, position)
19730     {
19731         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19732         
19733         
19734         
19735         if(!this.el){
19736             var cfg = Roo.apply({},  this.getAutoCreate());
19737             cfg.id = Roo.id();
19738             
19739             if (this.cls) {
19740                 cfg.cls += ' ' + this.cls;
19741             }
19742             if (this.style) {
19743                 cfg.style = this.style;
19744             }
19745             //Roo.log("adding to ");
19746             this.el = Roo.get(document.body).createChild(cfg, position);
19747 //            Roo.log(this.el);
19748         }
19749         
19750         this.contentEl = this.el.select('.popover-content',true).first();
19751         this.headerEl =  this.el.select('.popover-title',true).first();
19752         
19753         var nitems = [];
19754         if(typeof(this.items) != 'undefined'){
19755             var items = this.items;
19756             delete this.items;
19757
19758             for(var i =0;i < items.length;i++) {
19759                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19760             }
19761         }
19762
19763         this.items = nitems;
19764         
19765         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19766         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19767         
19768         
19769         
19770         this.initEvents();
19771     },
19772     
19773     resizeMask : function()
19774     {
19775         this.maskEl.setSize(
19776             Roo.lib.Dom.getViewWidth(true),
19777             Roo.lib.Dom.getViewHeight(true)
19778         );
19779     },
19780     
19781     initEvents : function()
19782     {
19783         
19784         if (!this.modal) { 
19785             Roo.bootstrap.Popover.register(this);
19786         }
19787          
19788         this.arrowEl = this.el.select('.arrow',true).first();
19789         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19790         this.el.enableDisplayMode('block');
19791         this.el.hide();
19792  
19793         
19794         if (this.over === false && !this.parent()) {
19795             return; 
19796         }
19797         if (this.triggers === false) {
19798             return;
19799         }
19800          
19801         // support parent
19802         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19803         var triggers = this.trigger ? this.trigger.split(' ') : [];
19804         Roo.each(triggers, function(trigger) {
19805         
19806             if (trigger == 'click') {
19807                 on_el.on('click', this.toggle, this);
19808             } else if (trigger != 'manual') {
19809                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19810                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19811       
19812                 on_el.on(eventIn  ,this.enter, this);
19813                 on_el.on(eventOut, this.leave, this);
19814             }
19815         }, this);
19816     },
19817     
19818     
19819     // private
19820     timeout : null,
19821     hoverState : null,
19822     
19823     toggle : function () {
19824         this.hoverState == 'in' ? this.leave() : this.enter();
19825     },
19826     
19827     enter : function () {
19828         
19829         clearTimeout(this.timeout);
19830     
19831         this.hoverState = 'in';
19832     
19833         if (!this.delay || !this.delay.show) {
19834             this.show();
19835             return;
19836         }
19837         var _t = this;
19838         this.timeout = setTimeout(function () {
19839             if (_t.hoverState == 'in') {
19840                 _t.show();
19841             }
19842         }, this.delay.show)
19843     },
19844     
19845     leave : function() {
19846         clearTimeout(this.timeout);
19847     
19848         this.hoverState = 'out';
19849     
19850         if (!this.delay || !this.delay.hide) {
19851             this.hide();
19852             return;
19853         }
19854         var _t = this;
19855         this.timeout = setTimeout(function () {
19856             if (_t.hoverState == 'out') {
19857                 _t.hide();
19858             }
19859         }, this.delay.hide)
19860     },
19861     /**
19862      * Show the popover
19863      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19864      * @param {string} (left|right|top|bottom) position
19865      */
19866     show : function (on_el, placement)
19867     {
19868         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19869         on_el = on_el || false; // default to false
19870          
19871         if (!on_el) {
19872             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19873                 on_el = this.parent().el;
19874             } else if (this.over) {
19875                 Roo.get(this.over);
19876             }
19877             
19878         }
19879         
19880         if (!this.el) {
19881             this.render(document.body);
19882         }
19883         
19884         
19885         this.el.removeClass([
19886             'fade','top','bottom', 'left', 'right','in',
19887             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19888         ]);
19889         
19890         if (this.title === false) {
19891             this.headerEl.hide();
19892         }
19893         
19894        
19895         this.el.show();
19896         this.el.dom.style.display = 'block';
19897          
19898         
19899         this.el.addClass(placement + ' roo-popover-' + placement);
19900
19901         if (on_el) {
19902             this.updatePosition();
19903              
19904         } else {
19905             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19906             var es = this.el.getSize();
19907             var x = Roo.lib.Dom.getViewWidth()/2;
19908             var y = Roo.lib.Dom.getViewHeight()/2;
19909             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19910             
19911         }
19912
19913         
19914         //var arrow = this.el.select('.arrow',true).first();
19915         //arrow.set(align[2], 
19916         
19917         this.el.addClass('in');
19918         
19919          
19920         
19921         this.hoverState = 'in';
19922         
19923         if (this.modal) {
19924             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19925             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19926             this.maskEl.dom.style.display = 'block';
19927             this.maskEl.addClass('show');
19928         }
19929         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19930  
19931         this.fireEvent('show', this);
19932         
19933     },
19934     /**
19935      * fire this manually after loading a grid in the table for example
19936      *
19937      */
19938     updatePosition : function()
19939     {
19940         this.el.addClass(placement + ' roo-popover-' + placement);
19941         
19942         if (!this.alignEl || !this.alignment) {
19943             return;
19944         }
19945         
19946         
19947         
19948         this.el.alignTo(this.alignEl , this.alignment[0],this.alignment[1]);
19949         
19950         // work out the pointy position.
19951         var p1 = this.alignment[0].split('-').pop().replace('?','');
19952         var xy = this.alignEl.getAnchorXY(p1, false);
19953         xy[0]+=2;xy[1]+=5;
19954         this.arrowEl.setXY(xy);
19955         
19956     },
19957     
19958     hide : function()
19959     {
19960         this.el.setXY([0,0]);
19961         this.el.removeClass('in');
19962         this.el.hide();
19963         this.hoverState = null;
19964         this.maskEl.hide(); // always..
19965         this.fireEvent('hide', this);
19966     }
19967     
19968 });
19969
19970
19971 Roo.apply(Roo.bootstrap.Popover, {
19972
19973     alignment : {
19974         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19975         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19976         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19977         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19978     },
19979     
19980     zIndex : 20001,
19981
19982     clickHander : false,
19983     
19984
19985     onMouseDown : function(e)
19986     {
19987         if (!e.getTarget(".roo-popover")) {
19988             this.hideAll();
19989         }
19990          
19991     },
19992     
19993     popups : [],
19994     
19995     register : function(popup)
19996     {
19997         if (!Roo.bootstrap.Popover.clickHandler) {
19998             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19999         }
20000         // hide other popups.
20001         this.hideAll();
20002         this.popups.push(popup);
20003     },
20004     hideAll : function()
20005     {
20006         this.popups.forEach(function(p) {
20007             p.hide();
20008         });
20009     }
20010
20011 });/*
20012  * - LGPL
20013  *
20014  * Card header - holder for the card header elements.
20015  * 
20016  */
20017
20018 /**
20019  * @class Roo.bootstrap.PopoverNav
20020  * @extends Roo.bootstrap.NavGroup
20021  * Bootstrap Popover header navigation class
20022  * @constructor
20023  * Create a new Popover Header Navigation 
20024  * @param {Object} config The config object
20025  */
20026
20027 Roo.bootstrap.PopoverNav = function(config){
20028     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20029 };
20030
20031 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20032     
20033     
20034     container_method : 'getPopoverHeader' 
20035     
20036      
20037     
20038     
20039    
20040 });
20041
20042  
20043
20044  /*
20045  * - LGPL
20046  *
20047  * Progress
20048  * 
20049  */
20050
20051 /**
20052  * @class Roo.bootstrap.Progress
20053  * @extends Roo.bootstrap.Component
20054  * Bootstrap Progress class
20055  * @cfg {Boolean} striped striped of the progress bar
20056  * @cfg {Boolean} active animated of the progress bar
20057  * 
20058  * 
20059  * @constructor
20060  * Create a new Progress
20061  * @param {Object} config The config object
20062  */
20063
20064 Roo.bootstrap.Progress = function(config){
20065     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20066 };
20067
20068 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20069     
20070     striped : false,
20071     active: false,
20072     
20073     getAutoCreate : function(){
20074         var cfg = {
20075             tag: 'div',
20076             cls: 'progress'
20077         };
20078         
20079         
20080         if(this.striped){
20081             cfg.cls += ' progress-striped';
20082         }
20083       
20084         if(this.active){
20085             cfg.cls += ' active';
20086         }
20087         
20088         
20089         return cfg;
20090     }
20091    
20092 });
20093
20094  
20095
20096  /*
20097  * - LGPL
20098  *
20099  * ProgressBar
20100  * 
20101  */
20102
20103 /**
20104  * @class Roo.bootstrap.ProgressBar
20105  * @extends Roo.bootstrap.Component
20106  * Bootstrap ProgressBar class
20107  * @cfg {Number} aria_valuenow aria-value now
20108  * @cfg {Number} aria_valuemin aria-value min
20109  * @cfg {Number} aria_valuemax aria-value max
20110  * @cfg {String} label label for the progress bar
20111  * @cfg {String} panel (success | info | warning | danger )
20112  * @cfg {String} role role of the progress bar
20113  * @cfg {String} sr_only text
20114  * 
20115  * 
20116  * @constructor
20117  * Create a new ProgressBar
20118  * @param {Object} config The config object
20119  */
20120
20121 Roo.bootstrap.ProgressBar = function(config){
20122     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20123 };
20124
20125 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20126     
20127     aria_valuenow : 0,
20128     aria_valuemin : 0,
20129     aria_valuemax : 100,
20130     label : false,
20131     panel : false,
20132     role : false,
20133     sr_only: false,
20134     
20135     getAutoCreate : function()
20136     {
20137         
20138         var cfg = {
20139             tag: 'div',
20140             cls: 'progress-bar',
20141             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20142         };
20143         
20144         if(this.sr_only){
20145             cfg.cn = {
20146                 tag: 'span',
20147                 cls: 'sr-only',
20148                 html: this.sr_only
20149             }
20150         }
20151         
20152         if(this.role){
20153             cfg.role = this.role;
20154         }
20155         
20156         if(this.aria_valuenow){
20157             cfg['aria-valuenow'] = this.aria_valuenow;
20158         }
20159         
20160         if(this.aria_valuemin){
20161             cfg['aria-valuemin'] = this.aria_valuemin;
20162         }
20163         
20164         if(this.aria_valuemax){
20165             cfg['aria-valuemax'] = this.aria_valuemax;
20166         }
20167         
20168         if(this.label && !this.sr_only){
20169             cfg.html = this.label;
20170         }
20171         
20172         if(this.panel){
20173             cfg.cls += ' progress-bar-' + this.panel;
20174         }
20175         
20176         return cfg;
20177     },
20178     
20179     update : function(aria_valuenow)
20180     {
20181         this.aria_valuenow = aria_valuenow;
20182         
20183         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20184     }
20185    
20186 });
20187
20188  
20189
20190  /*
20191  * - LGPL
20192  *
20193  * column
20194  * 
20195  */
20196
20197 /**
20198  * @class Roo.bootstrap.TabGroup
20199  * @extends Roo.bootstrap.Column
20200  * Bootstrap Column class
20201  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20202  * @cfg {Boolean} carousel true to make the group behave like a carousel
20203  * @cfg {Boolean} bullets show bullets for the panels
20204  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20205  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20206  * @cfg {Boolean} showarrow (true|false) show arrow default true
20207  * 
20208  * @constructor
20209  * Create a new TabGroup
20210  * @param {Object} config The config object
20211  */
20212
20213 Roo.bootstrap.TabGroup = function(config){
20214     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20215     if (!this.navId) {
20216         this.navId = Roo.id();
20217     }
20218     this.tabs = [];
20219     Roo.bootstrap.TabGroup.register(this);
20220     
20221 };
20222
20223 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20224     
20225     carousel : false,
20226     transition : false,
20227     bullets : 0,
20228     timer : 0,
20229     autoslide : false,
20230     slideFn : false,
20231     slideOnTouch : false,
20232     showarrow : true,
20233     
20234     getAutoCreate : function()
20235     {
20236         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20237         
20238         cfg.cls += ' tab-content';
20239         
20240         if (this.carousel) {
20241             cfg.cls += ' carousel slide';
20242             
20243             cfg.cn = [{
20244                cls : 'carousel-inner',
20245                cn : []
20246             }];
20247         
20248             if(this.bullets  && !Roo.isTouch){
20249                 
20250                 var bullets = {
20251                     cls : 'carousel-bullets',
20252                     cn : []
20253                 };
20254                
20255                 if(this.bullets_cls){
20256                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20257                 }
20258                 
20259                 bullets.cn.push({
20260                     cls : 'clear'
20261                 });
20262                 
20263                 cfg.cn[0].cn.push(bullets);
20264             }
20265             
20266             if(this.showarrow){
20267                 cfg.cn[0].cn.push({
20268                     tag : 'div',
20269                     class : 'carousel-arrow',
20270                     cn : [
20271                         {
20272                             tag : 'div',
20273                             class : 'carousel-prev',
20274                             cn : [
20275                                 {
20276                                     tag : 'i',
20277                                     class : 'fa fa-chevron-left'
20278                                 }
20279                             ]
20280                         },
20281                         {
20282                             tag : 'div',
20283                             class : 'carousel-next',
20284                             cn : [
20285                                 {
20286                                     tag : 'i',
20287                                     class : 'fa fa-chevron-right'
20288                                 }
20289                             ]
20290                         }
20291                     ]
20292                 });
20293             }
20294             
20295         }
20296         
20297         return cfg;
20298     },
20299     
20300     initEvents:  function()
20301     {
20302 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20303 //            this.el.on("touchstart", this.onTouchStart, this);
20304 //        }
20305         
20306         if(this.autoslide){
20307             var _this = this;
20308             
20309             this.slideFn = window.setInterval(function() {
20310                 _this.showPanelNext();
20311             }, this.timer);
20312         }
20313         
20314         if(this.showarrow){
20315             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20316             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20317         }
20318         
20319         
20320     },
20321     
20322 //    onTouchStart : function(e, el, o)
20323 //    {
20324 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20325 //            return;
20326 //        }
20327 //        
20328 //        this.showPanelNext();
20329 //    },
20330     
20331     
20332     getChildContainer : function()
20333     {
20334         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20335     },
20336     
20337     /**
20338     * register a Navigation item
20339     * @param {Roo.bootstrap.NavItem} the navitem to add
20340     */
20341     register : function(item)
20342     {
20343         this.tabs.push( item);
20344         item.navId = this.navId; // not really needed..
20345         this.addBullet();
20346     
20347     },
20348     
20349     getActivePanel : function()
20350     {
20351         var r = false;
20352         Roo.each(this.tabs, function(t) {
20353             if (t.active) {
20354                 r = t;
20355                 return false;
20356             }
20357             return null;
20358         });
20359         return r;
20360         
20361     },
20362     getPanelByName : function(n)
20363     {
20364         var r = false;
20365         Roo.each(this.tabs, function(t) {
20366             if (t.tabId == n) {
20367                 r = t;
20368                 return false;
20369             }
20370             return null;
20371         });
20372         return r;
20373     },
20374     indexOfPanel : function(p)
20375     {
20376         var r = false;
20377         Roo.each(this.tabs, function(t,i) {
20378             if (t.tabId == p.tabId) {
20379                 r = i;
20380                 return false;
20381             }
20382             return null;
20383         });
20384         return r;
20385     },
20386     /**
20387      * show a specific panel
20388      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20389      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20390      */
20391     showPanel : function (pan)
20392     {
20393         if(this.transition || typeof(pan) == 'undefined'){
20394             Roo.log("waiting for the transitionend");
20395             return false;
20396         }
20397         
20398         if (typeof(pan) == 'number') {
20399             pan = this.tabs[pan];
20400         }
20401         
20402         if (typeof(pan) == 'string') {
20403             pan = this.getPanelByName(pan);
20404         }
20405         
20406         var cur = this.getActivePanel();
20407         
20408         if(!pan || !cur){
20409             Roo.log('pan or acitve pan is undefined');
20410             return false;
20411         }
20412         
20413         if (pan.tabId == this.getActivePanel().tabId) {
20414             return true;
20415         }
20416         
20417         if (false === cur.fireEvent('beforedeactivate')) {
20418             return false;
20419         }
20420         
20421         if(this.bullets > 0 && !Roo.isTouch){
20422             this.setActiveBullet(this.indexOfPanel(pan));
20423         }
20424         
20425         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20426             
20427             //class="carousel-item carousel-item-next carousel-item-left"
20428             
20429             this.transition = true;
20430             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20431             var lr = dir == 'next' ? 'left' : 'right';
20432             pan.el.addClass(dir); // or prev
20433             pan.el.addClass('carousel-item-' + dir); // or prev
20434             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20435             cur.el.addClass(lr); // or right
20436             pan.el.addClass(lr);
20437             cur.el.addClass('carousel-item-' +lr); // or right
20438             pan.el.addClass('carousel-item-' +lr);
20439             
20440             
20441             var _this = this;
20442             cur.el.on('transitionend', function() {
20443                 Roo.log("trans end?");
20444                 
20445                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20446                 pan.setActive(true);
20447                 
20448                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20449                 cur.setActive(false);
20450                 
20451                 _this.transition = false;
20452                 
20453             }, this, { single:  true } );
20454             
20455             return true;
20456         }
20457         
20458         cur.setActive(false);
20459         pan.setActive(true);
20460         
20461         return true;
20462         
20463     },
20464     showPanelNext : function()
20465     {
20466         var i = this.indexOfPanel(this.getActivePanel());
20467         
20468         if (i >= this.tabs.length - 1 && !this.autoslide) {
20469             return;
20470         }
20471         
20472         if (i >= this.tabs.length - 1 && this.autoslide) {
20473             i = -1;
20474         }
20475         
20476         this.showPanel(this.tabs[i+1]);
20477     },
20478     
20479     showPanelPrev : function()
20480     {
20481         var i = this.indexOfPanel(this.getActivePanel());
20482         
20483         if (i  < 1 && !this.autoslide) {
20484             return;
20485         }
20486         
20487         if (i < 1 && this.autoslide) {
20488             i = this.tabs.length;
20489         }
20490         
20491         this.showPanel(this.tabs[i-1]);
20492     },
20493     
20494     
20495     addBullet: function()
20496     {
20497         if(!this.bullets || Roo.isTouch){
20498             return;
20499         }
20500         var ctr = this.el.select('.carousel-bullets',true).first();
20501         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20502         var bullet = ctr.createChild({
20503             cls : 'bullet bullet-' + i
20504         },ctr.dom.lastChild);
20505         
20506         
20507         var _this = this;
20508         
20509         bullet.on('click', (function(e, el, o, ii, t){
20510
20511             e.preventDefault();
20512
20513             this.showPanel(ii);
20514
20515             if(this.autoslide && this.slideFn){
20516                 clearInterval(this.slideFn);
20517                 this.slideFn = window.setInterval(function() {
20518                     _this.showPanelNext();
20519                 }, this.timer);
20520             }
20521
20522         }).createDelegate(this, [i, bullet], true));
20523                 
20524         
20525     },
20526      
20527     setActiveBullet : function(i)
20528     {
20529         if(Roo.isTouch){
20530             return;
20531         }
20532         
20533         Roo.each(this.el.select('.bullet', true).elements, function(el){
20534             el.removeClass('selected');
20535         });
20536
20537         var bullet = this.el.select('.bullet-' + i, true).first();
20538         
20539         if(!bullet){
20540             return;
20541         }
20542         
20543         bullet.addClass('selected');
20544     }
20545     
20546     
20547   
20548 });
20549
20550  
20551
20552  
20553  
20554 Roo.apply(Roo.bootstrap.TabGroup, {
20555     
20556     groups: {},
20557      /**
20558     * register a Navigation Group
20559     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20560     */
20561     register : function(navgrp)
20562     {
20563         this.groups[navgrp.navId] = navgrp;
20564         
20565     },
20566     /**
20567     * fetch a Navigation Group based on the navigation ID
20568     * if one does not exist , it will get created.
20569     * @param {string} the navgroup to add
20570     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20571     */
20572     get: function(navId) {
20573         if (typeof(this.groups[navId]) == 'undefined') {
20574             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20575         }
20576         return this.groups[navId] ;
20577     }
20578     
20579     
20580     
20581 });
20582
20583  /*
20584  * - LGPL
20585  *
20586  * TabPanel
20587  * 
20588  */
20589
20590 /**
20591  * @class Roo.bootstrap.TabPanel
20592  * @extends Roo.bootstrap.Component
20593  * Bootstrap TabPanel class
20594  * @cfg {Boolean} active panel active
20595  * @cfg {String} html panel content
20596  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20597  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20598  * @cfg {String} href click to link..
20599  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20600  * 
20601  * 
20602  * @constructor
20603  * Create a new TabPanel
20604  * @param {Object} config The config object
20605  */
20606
20607 Roo.bootstrap.TabPanel = function(config){
20608     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20609     this.addEvents({
20610         /**
20611              * @event changed
20612              * Fires when the active status changes
20613              * @param {Roo.bootstrap.TabPanel} this
20614              * @param {Boolean} state the new state
20615             
20616          */
20617         'changed': true,
20618         /**
20619              * @event beforedeactivate
20620              * Fires before a tab is de-activated - can be used to do validation on a form.
20621              * @param {Roo.bootstrap.TabPanel} this
20622              * @return {Boolean} false if there is an error
20623             
20624          */
20625         'beforedeactivate': true
20626      });
20627     
20628     this.tabId = this.tabId || Roo.id();
20629   
20630 };
20631
20632 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20633     
20634     active: false,
20635     html: false,
20636     tabId: false,
20637     navId : false,
20638     href : '',
20639     touchSlide : false,
20640     getAutoCreate : function(){
20641         
20642         
20643         var cfg = {
20644             tag: 'div',
20645             // item is needed for carousel - not sure if it has any effect otherwise
20646             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20647             html: this.html || ''
20648         };
20649         
20650         if(this.active){
20651             cfg.cls += ' active';
20652         }
20653         
20654         if(this.tabId){
20655             cfg.tabId = this.tabId;
20656         }
20657         
20658         
20659         
20660         return cfg;
20661     },
20662     
20663     initEvents:  function()
20664     {
20665         var p = this.parent();
20666         
20667         this.navId = this.navId || p.navId;
20668         
20669         if (typeof(this.navId) != 'undefined') {
20670             // not really needed.. but just in case.. parent should be a NavGroup.
20671             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20672             
20673             tg.register(this);
20674             
20675             var i = tg.tabs.length - 1;
20676             
20677             if(this.active && tg.bullets > 0 && i < tg.bullets){
20678                 tg.setActiveBullet(i);
20679             }
20680         }
20681         
20682         this.el.on('click', this.onClick, this);
20683         
20684         if(Roo.isTouch && this.touchSlide){
20685             this.el.on("touchstart", this.onTouchStart, this);
20686             this.el.on("touchmove", this.onTouchMove, this);
20687             this.el.on("touchend", this.onTouchEnd, this);
20688         }
20689         
20690     },
20691     
20692     onRender : function(ct, position)
20693     {
20694         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20695     },
20696     
20697     setActive : function(state)
20698     {
20699         Roo.log("panel - set active " + this.tabId + "=" + state);
20700         
20701         this.active = state;
20702         if (!state) {
20703             this.el.removeClass('active');
20704             
20705         } else  if (!this.el.hasClass('active')) {
20706             this.el.addClass('active');
20707         }
20708         
20709         this.fireEvent('changed', this, state);
20710     },
20711     
20712     onClick : function(e)
20713     {
20714         e.preventDefault();
20715         
20716         if(!this.href.length){
20717             return;
20718         }
20719         
20720         window.location.href = this.href;
20721     },
20722     
20723     startX : 0,
20724     startY : 0,
20725     endX : 0,
20726     endY : 0,
20727     swiping : false,
20728     
20729     onTouchStart : function(e)
20730     {
20731         this.swiping = false;
20732         
20733         this.startX = e.browserEvent.touches[0].clientX;
20734         this.startY = e.browserEvent.touches[0].clientY;
20735     },
20736     
20737     onTouchMove : function(e)
20738     {
20739         this.swiping = true;
20740         
20741         this.endX = e.browserEvent.touches[0].clientX;
20742         this.endY = e.browserEvent.touches[0].clientY;
20743     },
20744     
20745     onTouchEnd : function(e)
20746     {
20747         if(!this.swiping){
20748             this.onClick(e);
20749             return;
20750         }
20751         
20752         var tabGroup = this.parent();
20753         
20754         if(this.endX > this.startX){ // swiping right
20755             tabGroup.showPanelPrev();
20756             return;
20757         }
20758         
20759         if(this.startX > this.endX){ // swiping left
20760             tabGroup.showPanelNext();
20761             return;
20762         }
20763     }
20764     
20765     
20766 });
20767  
20768
20769  
20770
20771  /*
20772  * - LGPL
20773  *
20774  * DateField
20775  * 
20776  */
20777
20778 /**
20779  * @class Roo.bootstrap.DateField
20780  * @extends Roo.bootstrap.Input
20781  * Bootstrap DateField class
20782  * @cfg {Number} weekStart default 0
20783  * @cfg {String} viewMode default empty, (months|years)
20784  * @cfg {String} minViewMode default empty, (months|years)
20785  * @cfg {Number} startDate default -Infinity
20786  * @cfg {Number} endDate default Infinity
20787  * @cfg {Boolean} todayHighlight default false
20788  * @cfg {Boolean} todayBtn default false
20789  * @cfg {Boolean} calendarWeeks default false
20790  * @cfg {Object} daysOfWeekDisabled default empty
20791  * @cfg {Boolean} singleMode default false (true | false)
20792  * 
20793  * @cfg {Boolean} keyboardNavigation default true
20794  * @cfg {String} language default en
20795  * 
20796  * @constructor
20797  * Create a new DateField
20798  * @param {Object} config The config object
20799  */
20800
20801 Roo.bootstrap.DateField = function(config){
20802     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20803      this.addEvents({
20804             /**
20805              * @event show
20806              * Fires when this field show.
20807              * @param {Roo.bootstrap.DateField} this
20808              * @param {Mixed} date The date value
20809              */
20810             show : true,
20811             /**
20812              * @event show
20813              * Fires when this field hide.
20814              * @param {Roo.bootstrap.DateField} this
20815              * @param {Mixed} date The date value
20816              */
20817             hide : true,
20818             /**
20819              * @event select
20820              * Fires when select a date.
20821              * @param {Roo.bootstrap.DateField} this
20822              * @param {Mixed} date The date value
20823              */
20824             select : true,
20825             /**
20826              * @event beforeselect
20827              * Fires when before select a date.
20828              * @param {Roo.bootstrap.DateField} this
20829              * @param {Mixed} date The date value
20830              */
20831             beforeselect : true
20832         });
20833 };
20834
20835 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20836     
20837     /**
20838      * @cfg {String} format
20839      * The default date format string which can be overriden for localization support.  The format must be
20840      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20841      */
20842     format : "m/d/y",
20843     /**
20844      * @cfg {String} altFormats
20845      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20846      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20847      */
20848     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20849     
20850     weekStart : 0,
20851     
20852     viewMode : '',
20853     
20854     minViewMode : '',
20855     
20856     todayHighlight : false,
20857     
20858     todayBtn: false,
20859     
20860     language: 'en',
20861     
20862     keyboardNavigation: true,
20863     
20864     calendarWeeks: false,
20865     
20866     startDate: -Infinity,
20867     
20868     endDate: Infinity,
20869     
20870     daysOfWeekDisabled: [],
20871     
20872     _events: [],
20873     
20874     singleMode : false,
20875     
20876     UTCDate: function()
20877     {
20878         return new Date(Date.UTC.apply(Date, arguments));
20879     },
20880     
20881     UTCToday: function()
20882     {
20883         var today = new Date();
20884         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20885     },
20886     
20887     getDate: function() {
20888             var d = this.getUTCDate();
20889             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20890     },
20891     
20892     getUTCDate: function() {
20893             return this.date;
20894     },
20895     
20896     setDate: function(d) {
20897             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20898     },
20899     
20900     setUTCDate: function(d) {
20901             this.date = d;
20902             this.setValue(this.formatDate(this.date));
20903     },
20904         
20905     onRender: function(ct, position)
20906     {
20907         
20908         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20909         
20910         this.language = this.language || 'en';
20911         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20912         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20913         
20914         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20915         this.format = this.format || 'm/d/y';
20916         this.isInline = false;
20917         this.isInput = true;
20918         this.component = this.el.select('.add-on', true).first() || false;
20919         this.component = (this.component && this.component.length === 0) ? false : this.component;
20920         this.hasInput = this.component && this.inputEl().length;
20921         
20922         if (typeof(this.minViewMode === 'string')) {
20923             switch (this.minViewMode) {
20924                 case 'months':
20925                     this.minViewMode = 1;
20926                     break;
20927                 case 'years':
20928                     this.minViewMode = 2;
20929                     break;
20930                 default:
20931                     this.minViewMode = 0;
20932                     break;
20933             }
20934         }
20935         
20936         if (typeof(this.viewMode === 'string')) {
20937             switch (this.viewMode) {
20938                 case 'months':
20939                     this.viewMode = 1;
20940                     break;
20941                 case 'years':
20942                     this.viewMode = 2;
20943                     break;
20944                 default:
20945                     this.viewMode = 0;
20946                     break;
20947             }
20948         }
20949                 
20950         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20951         
20952 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20953         
20954         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20955         
20956         this.picker().on('mousedown', this.onMousedown, this);
20957         this.picker().on('click', this.onClick, this);
20958         
20959         this.picker().addClass('datepicker-dropdown');
20960         
20961         this.startViewMode = this.viewMode;
20962         
20963         if(this.singleMode){
20964             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20965                 v.setVisibilityMode(Roo.Element.DISPLAY);
20966                 v.hide();
20967             });
20968             
20969             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20970                 v.setStyle('width', '189px');
20971             });
20972         }
20973         
20974         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20975             if(!this.calendarWeeks){
20976                 v.remove();
20977                 return;
20978             }
20979             
20980             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20981             v.attr('colspan', function(i, val){
20982                 return parseInt(val) + 1;
20983             });
20984         });
20985                         
20986         
20987         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20988         
20989         this.setStartDate(this.startDate);
20990         this.setEndDate(this.endDate);
20991         
20992         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20993         
20994         this.fillDow();
20995         this.fillMonths();
20996         this.update();
20997         this.showMode();
20998         
20999         if(this.isInline) {
21000             this.showPopup();
21001         }
21002     },
21003     
21004     picker : function()
21005     {
21006         return this.pickerEl;
21007 //        return this.el.select('.datepicker', true).first();
21008     },
21009     
21010     fillDow: function()
21011     {
21012         var dowCnt = this.weekStart;
21013         
21014         var dow = {
21015             tag: 'tr',
21016             cn: [
21017                 
21018             ]
21019         };
21020         
21021         if(this.calendarWeeks){
21022             dow.cn.push({
21023                 tag: 'th',
21024                 cls: 'cw',
21025                 html: '&nbsp;'
21026             })
21027         }
21028         
21029         while (dowCnt < this.weekStart + 7) {
21030             dow.cn.push({
21031                 tag: 'th',
21032                 cls: 'dow',
21033                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21034             });
21035         }
21036         
21037         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21038     },
21039     
21040     fillMonths: function()
21041     {    
21042         var i = 0;
21043         var months = this.picker().select('>.datepicker-months td', true).first();
21044         
21045         months.dom.innerHTML = '';
21046         
21047         while (i < 12) {
21048             var month = {
21049                 tag: 'span',
21050                 cls: 'month',
21051                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21052             };
21053             
21054             months.createChild(month);
21055         }
21056         
21057     },
21058     
21059     update: function()
21060     {
21061         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;
21062         
21063         if (this.date < this.startDate) {
21064             this.viewDate = new Date(this.startDate);
21065         } else if (this.date > this.endDate) {
21066             this.viewDate = new Date(this.endDate);
21067         } else {
21068             this.viewDate = new Date(this.date);
21069         }
21070         
21071         this.fill();
21072     },
21073     
21074     fill: function() 
21075     {
21076         var d = new Date(this.viewDate),
21077                 year = d.getUTCFullYear(),
21078                 month = d.getUTCMonth(),
21079                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21080                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21081                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21082                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21083                 currentDate = this.date && this.date.valueOf(),
21084                 today = this.UTCToday();
21085         
21086         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21087         
21088 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21089         
21090 //        this.picker.select('>tfoot th.today').
21091 //                                              .text(dates[this.language].today)
21092 //                                              .toggle(this.todayBtn !== false);
21093     
21094         this.updateNavArrows();
21095         this.fillMonths();
21096                                                 
21097         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21098         
21099         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21100          
21101         prevMonth.setUTCDate(day);
21102         
21103         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21104         
21105         var nextMonth = new Date(prevMonth);
21106         
21107         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21108         
21109         nextMonth = nextMonth.valueOf();
21110         
21111         var fillMonths = false;
21112         
21113         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21114         
21115         while(prevMonth.valueOf() <= nextMonth) {
21116             var clsName = '';
21117             
21118             if (prevMonth.getUTCDay() === this.weekStart) {
21119                 if(fillMonths){
21120                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21121                 }
21122                     
21123                 fillMonths = {
21124                     tag: 'tr',
21125                     cn: []
21126                 };
21127                 
21128                 if(this.calendarWeeks){
21129                     // ISO 8601: First week contains first thursday.
21130                     // ISO also states week starts on Monday, but we can be more abstract here.
21131                     var
21132                     // Start of current week: based on weekstart/current date
21133                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21134                     // Thursday of this week
21135                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21136                     // First Thursday of year, year from thursday
21137                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21138                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21139                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21140                     
21141                     fillMonths.cn.push({
21142                         tag: 'td',
21143                         cls: 'cw',
21144                         html: calWeek
21145                     });
21146                 }
21147             }
21148             
21149             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21150                 clsName += ' old';
21151             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21152                 clsName += ' new';
21153             }
21154             if (this.todayHighlight &&
21155                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21156                 prevMonth.getUTCMonth() == today.getMonth() &&
21157                 prevMonth.getUTCDate() == today.getDate()) {
21158                 clsName += ' today';
21159             }
21160             
21161             if (currentDate && prevMonth.valueOf() === currentDate) {
21162                 clsName += ' active';
21163             }
21164             
21165             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21166                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21167                     clsName += ' disabled';
21168             }
21169             
21170             fillMonths.cn.push({
21171                 tag: 'td',
21172                 cls: 'day ' + clsName,
21173                 html: prevMonth.getDate()
21174             });
21175             
21176             prevMonth.setDate(prevMonth.getDate()+1);
21177         }
21178           
21179         var currentYear = this.date && this.date.getUTCFullYear();
21180         var currentMonth = this.date && this.date.getUTCMonth();
21181         
21182         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21183         
21184         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21185             v.removeClass('active');
21186             
21187             if(currentYear === year && k === currentMonth){
21188                 v.addClass('active');
21189             }
21190             
21191             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21192                 v.addClass('disabled');
21193             }
21194             
21195         });
21196         
21197         
21198         year = parseInt(year/10, 10) * 10;
21199         
21200         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21201         
21202         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21203         
21204         year -= 1;
21205         for (var i = -1; i < 11; i++) {
21206             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21207                 tag: 'span',
21208                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21209                 html: year
21210             });
21211             
21212             year += 1;
21213         }
21214     },
21215     
21216     showMode: function(dir) 
21217     {
21218         if (dir) {
21219             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21220         }
21221         
21222         Roo.each(this.picker().select('>div',true).elements, function(v){
21223             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21224             v.hide();
21225         });
21226         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21227     },
21228     
21229     place: function()
21230     {
21231         if(this.isInline) {
21232             return;
21233         }
21234         
21235         this.picker().removeClass(['bottom', 'top']);
21236         
21237         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21238             /*
21239              * place to the top of element!
21240              *
21241              */
21242             
21243             this.picker().addClass('top');
21244             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21245             
21246             return;
21247         }
21248         
21249         this.picker().addClass('bottom');
21250         
21251         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21252     },
21253     
21254     parseDate : function(value)
21255     {
21256         if(!value || value instanceof Date){
21257             return value;
21258         }
21259         var v = Date.parseDate(value, this.format);
21260         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21261             v = Date.parseDate(value, 'Y-m-d');
21262         }
21263         if(!v && this.altFormats){
21264             if(!this.altFormatsArray){
21265                 this.altFormatsArray = this.altFormats.split("|");
21266             }
21267             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21268                 v = Date.parseDate(value, this.altFormatsArray[i]);
21269             }
21270         }
21271         return v;
21272     },
21273     
21274     formatDate : function(date, fmt)
21275     {   
21276         return (!date || !(date instanceof Date)) ?
21277         date : date.dateFormat(fmt || this.format);
21278     },
21279     
21280     onFocus : function()
21281     {
21282         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21283         this.showPopup();
21284     },
21285     
21286     onBlur : function()
21287     {
21288         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21289         
21290         var d = this.inputEl().getValue();
21291         
21292         this.setValue(d);
21293                 
21294         this.hidePopup();
21295     },
21296     
21297     showPopup : function()
21298     {
21299         this.picker().show();
21300         this.update();
21301         this.place();
21302         
21303         this.fireEvent('showpopup', this, this.date);
21304     },
21305     
21306     hidePopup : function()
21307     {
21308         if(this.isInline) {
21309             return;
21310         }
21311         this.picker().hide();
21312         this.viewMode = this.startViewMode;
21313         this.showMode();
21314         
21315         this.fireEvent('hidepopup', this, this.date);
21316         
21317     },
21318     
21319     onMousedown: function(e)
21320     {
21321         e.stopPropagation();
21322         e.preventDefault();
21323     },
21324     
21325     keyup: function(e)
21326     {
21327         Roo.bootstrap.DateField.superclass.keyup.call(this);
21328         this.update();
21329     },
21330
21331     setValue: function(v)
21332     {
21333         if(this.fireEvent('beforeselect', this, v) !== false){
21334             var d = new Date(this.parseDate(v) ).clearTime();
21335         
21336             if(isNaN(d.getTime())){
21337                 this.date = this.viewDate = '';
21338                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21339                 return;
21340             }
21341
21342             v = this.formatDate(d);
21343
21344             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21345
21346             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21347
21348             this.update();
21349
21350             this.fireEvent('select', this, this.date);
21351         }
21352     },
21353     
21354     getValue: function()
21355     {
21356         return this.formatDate(this.date);
21357     },
21358     
21359     fireKey: function(e)
21360     {
21361         if (!this.picker().isVisible()){
21362             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21363                 this.showPopup();
21364             }
21365             return;
21366         }
21367         
21368         var dateChanged = false,
21369         dir, day, month,
21370         newDate, newViewDate;
21371         
21372         switch(e.keyCode){
21373             case 27: // escape
21374                 this.hidePopup();
21375                 e.preventDefault();
21376                 break;
21377             case 37: // left
21378             case 39: // right
21379                 if (!this.keyboardNavigation) {
21380                     break;
21381                 }
21382                 dir = e.keyCode == 37 ? -1 : 1;
21383                 
21384                 if (e.ctrlKey){
21385                     newDate = this.moveYear(this.date, dir);
21386                     newViewDate = this.moveYear(this.viewDate, dir);
21387                 } else if (e.shiftKey){
21388                     newDate = this.moveMonth(this.date, dir);
21389                     newViewDate = this.moveMonth(this.viewDate, dir);
21390                 } else {
21391                     newDate = new Date(this.date);
21392                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21393                     newViewDate = new Date(this.viewDate);
21394                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21395                 }
21396                 if (this.dateWithinRange(newDate)){
21397                     this.date = newDate;
21398                     this.viewDate = newViewDate;
21399                     this.setValue(this.formatDate(this.date));
21400 //                    this.update();
21401                     e.preventDefault();
21402                     dateChanged = true;
21403                 }
21404                 break;
21405             case 38: // up
21406             case 40: // down
21407                 if (!this.keyboardNavigation) {
21408                     break;
21409                 }
21410                 dir = e.keyCode == 38 ? -1 : 1;
21411                 if (e.ctrlKey){
21412                     newDate = this.moveYear(this.date, dir);
21413                     newViewDate = this.moveYear(this.viewDate, dir);
21414                 } else if (e.shiftKey){
21415                     newDate = this.moveMonth(this.date, dir);
21416                     newViewDate = this.moveMonth(this.viewDate, dir);
21417                 } else {
21418                     newDate = new Date(this.date);
21419                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21420                     newViewDate = new Date(this.viewDate);
21421                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21422                 }
21423                 if (this.dateWithinRange(newDate)){
21424                     this.date = newDate;
21425                     this.viewDate = newViewDate;
21426                     this.setValue(this.formatDate(this.date));
21427 //                    this.update();
21428                     e.preventDefault();
21429                     dateChanged = true;
21430                 }
21431                 break;
21432             case 13: // enter
21433                 this.setValue(this.formatDate(this.date));
21434                 this.hidePopup();
21435                 e.preventDefault();
21436                 break;
21437             case 9: // tab
21438                 this.setValue(this.formatDate(this.date));
21439                 this.hidePopup();
21440                 break;
21441             case 16: // shift
21442             case 17: // ctrl
21443             case 18: // alt
21444                 break;
21445             default :
21446                 this.hidePopup();
21447                 
21448         }
21449     },
21450     
21451     
21452     onClick: function(e) 
21453     {
21454         e.stopPropagation();
21455         e.preventDefault();
21456         
21457         var target = e.getTarget();
21458         
21459         if(target.nodeName.toLowerCase() === 'i'){
21460             target = Roo.get(target).dom.parentNode;
21461         }
21462         
21463         var nodeName = target.nodeName;
21464         var className = target.className;
21465         var html = target.innerHTML;
21466         //Roo.log(nodeName);
21467         
21468         switch(nodeName.toLowerCase()) {
21469             case 'th':
21470                 switch(className) {
21471                     case 'switch':
21472                         this.showMode(1);
21473                         break;
21474                     case 'prev':
21475                     case 'next':
21476                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21477                         switch(this.viewMode){
21478                                 case 0:
21479                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21480                                         break;
21481                                 case 1:
21482                                 case 2:
21483                                         this.viewDate = this.moveYear(this.viewDate, dir);
21484                                         break;
21485                         }
21486                         this.fill();
21487                         break;
21488                     case 'today':
21489                         var date = new Date();
21490                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21491 //                        this.fill()
21492                         this.setValue(this.formatDate(this.date));
21493                         
21494                         this.hidePopup();
21495                         break;
21496                 }
21497                 break;
21498             case 'span':
21499                 if (className.indexOf('disabled') < 0) {
21500                     this.viewDate.setUTCDate(1);
21501                     if (className.indexOf('month') > -1) {
21502                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21503                     } else {
21504                         var year = parseInt(html, 10) || 0;
21505                         this.viewDate.setUTCFullYear(year);
21506                         
21507                     }
21508                     
21509                     if(this.singleMode){
21510                         this.setValue(this.formatDate(this.viewDate));
21511                         this.hidePopup();
21512                         return;
21513                     }
21514                     
21515                     this.showMode(-1);
21516                     this.fill();
21517                 }
21518                 break;
21519                 
21520             case 'td':
21521                 //Roo.log(className);
21522                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21523                     var day = parseInt(html, 10) || 1;
21524                     var year = this.viewDate.getUTCFullYear(),
21525                         month = this.viewDate.getUTCMonth();
21526
21527                     if (className.indexOf('old') > -1) {
21528                         if(month === 0 ){
21529                             month = 11;
21530                             year -= 1;
21531                         }else{
21532                             month -= 1;
21533                         }
21534                     } else if (className.indexOf('new') > -1) {
21535                         if (month == 11) {
21536                             month = 0;
21537                             year += 1;
21538                         } else {
21539                             month += 1;
21540                         }
21541                     }
21542                     //Roo.log([year,month,day]);
21543                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21544                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21545 //                    this.fill();
21546                     //Roo.log(this.formatDate(this.date));
21547                     this.setValue(this.formatDate(this.date));
21548                     this.hidePopup();
21549                 }
21550                 break;
21551         }
21552     },
21553     
21554     setStartDate: function(startDate)
21555     {
21556         this.startDate = startDate || -Infinity;
21557         if (this.startDate !== -Infinity) {
21558             this.startDate = this.parseDate(this.startDate);
21559         }
21560         this.update();
21561         this.updateNavArrows();
21562     },
21563
21564     setEndDate: function(endDate)
21565     {
21566         this.endDate = endDate || Infinity;
21567         if (this.endDate !== Infinity) {
21568             this.endDate = this.parseDate(this.endDate);
21569         }
21570         this.update();
21571         this.updateNavArrows();
21572     },
21573     
21574     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21575     {
21576         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21577         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21578             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21579         }
21580         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21581             return parseInt(d, 10);
21582         });
21583         this.update();
21584         this.updateNavArrows();
21585     },
21586     
21587     updateNavArrows: function() 
21588     {
21589         if(this.singleMode){
21590             return;
21591         }
21592         
21593         var d = new Date(this.viewDate),
21594         year = d.getUTCFullYear(),
21595         month = d.getUTCMonth();
21596         
21597         Roo.each(this.picker().select('.prev', true).elements, function(v){
21598             v.show();
21599             switch (this.viewMode) {
21600                 case 0:
21601
21602                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21603                         v.hide();
21604                     }
21605                     break;
21606                 case 1:
21607                 case 2:
21608                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21609                         v.hide();
21610                     }
21611                     break;
21612             }
21613         });
21614         
21615         Roo.each(this.picker().select('.next', true).elements, function(v){
21616             v.show();
21617             switch (this.viewMode) {
21618                 case 0:
21619
21620                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21621                         v.hide();
21622                     }
21623                     break;
21624                 case 1:
21625                 case 2:
21626                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21627                         v.hide();
21628                     }
21629                     break;
21630             }
21631         })
21632     },
21633     
21634     moveMonth: function(date, dir)
21635     {
21636         if (!dir) {
21637             return date;
21638         }
21639         var new_date = new Date(date.valueOf()),
21640         day = new_date.getUTCDate(),
21641         month = new_date.getUTCMonth(),
21642         mag = Math.abs(dir),
21643         new_month, test;
21644         dir = dir > 0 ? 1 : -1;
21645         if (mag == 1){
21646             test = dir == -1
21647             // If going back one month, make sure month is not current month
21648             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21649             ? function(){
21650                 return new_date.getUTCMonth() == month;
21651             }
21652             // If going forward one month, make sure month is as expected
21653             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21654             : function(){
21655                 return new_date.getUTCMonth() != new_month;
21656             };
21657             new_month = month + dir;
21658             new_date.setUTCMonth(new_month);
21659             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21660             if (new_month < 0 || new_month > 11) {
21661                 new_month = (new_month + 12) % 12;
21662             }
21663         } else {
21664             // For magnitudes >1, move one month at a time...
21665             for (var i=0; i<mag; i++) {
21666                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21667                 new_date = this.moveMonth(new_date, dir);
21668             }
21669             // ...then reset the day, keeping it in the new month
21670             new_month = new_date.getUTCMonth();
21671             new_date.setUTCDate(day);
21672             test = function(){
21673                 return new_month != new_date.getUTCMonth();
21674             };
21675         }
21676         // Common date-resetting loop -- if date is beyond end of month, make it
21677         // end of month
21678         while (test()){
21679             new_date.setUTCDate(--day);
21680             new_date.setUTCMonth(new_month);
21681         }
21682         return new_date;
21683     },
21684
21685     moveYear: function(date, dir)
21686     {
21687         return this.moveMonth(date, dir*12);
21688     },
21689
21690     dateWithinRange: function(date)
21691     {
21692         return date >= this.startDate && date <= this.endDate;
21693     },
21694
21695     
21696     remove: function() 
21697     {
21698         this.picker().remove();
21699     },
21700     
21701     validateValue : function(value)
21702     {
21703         if(this.getVisibilityEl().hasClass('hidden')){
21704             return true;
21705         }
21706         
21707         if(value.length < 1)  {
21708             if(this.allowBlank){
21709                 return true;
21710             }
21711             return false;
21712         }
21713         
21714         if(value.length < this.minLength){
21715             return false;
21716         }
21717         if(value.length > this.maxLength){
21718             return false;
21719         }
21720         if(this.vtype){
21721             var vt = Roo.form.VTypes;
21722             if(!vt[this.vtype](value, this)){
21723                 return false;
21724             }
21725         }
21726         if(typeof this.validator == "function"){
21727             var msg = this.validator(value);
21728             if(msg !== true){
21729                 return false;
21730             }
21731         }
21732         
21733         if(this.regex && !this.regex.test(value)){
21734             return false;
21735         }
21736         
21737         if(typeof(this.parseDate(value)) == 'undefined'){
21738             return false;
21739         }
21740         
21741         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21742             return false;
21743         }      
21744         
21745         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21746             return false;
21747         } 
21748         
21749         
21750         return true;
21751     },
21752     
21753     reset : function()
21754     {
21755         this.date = this.viewDate = '';
21756         
21757         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21758     }
21759    
21760 });
21761
21762 Roo.apply(Roo.bootstrap.DateField,  {
21763     
21764     head : {
21765         tag: 'thead',
21766         cn: [
21767         {
21768             tag: 'tr',
21769             cn: [
21770             {
21771                 tag: 'th',
21772                 cls: 'prev',
21773                 html: '<i class="fa fa-arrow-left"/>'
21774             },
21775             {
21776                 tag: 'th',
21777                 cls: 'switch',
21778                 colspan: '5'
21779             },
21780             {
21781                 tag: 'th',
21782                 cls: 'next',
21783                 html: '<i class="fa fa-arrow-right"/>'
21784             }
21785
21786             ]
21787         }
21788         ]
21789     },
21790     
21791     content : {
21792         tag: 'tbody',
21793         cn: [
21794         {
21795             tag: 'tr',
21796             cn: [
21797             {
21798                 tag: 'td',
21799                 colspan: '7'
21800             }
21801             ]
21802         }
21803         ]
21804     },
21805     
21806     footer : {
21807         tag: 'tfoot',
21808         cn: [
21809         {
21810             tag: 'tr',
21811             cn: [
21812             {
21813                 tag: 'th',
21814                 colspan: '7',
21815                 cls: 'today'
21816             }
21817                     
21818             ]
21819         }
21820         ]
21821     },
21822     
21823     dates:{
21824         en: {
21825             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21826             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21827             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21828             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21829             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21830             today: "Today"
21831         }
21832     },
21833     
21834     modes: [
21835     {
21836         clsName: 'days',
21837         navFnc: 'Month',
21838         navStep: 1
21839     },
21840     {
21841         clsName: 'months',
21842         navFnc: 'FullYear',
21843         navStep: 1
21844     },
21845     {
21846         clsName: 'years',
21847         navFnc: 'FullYear',
21848         navStep: 10
21849     }]
21850 });
21851
21852 Roo.apply(Roo.bootstrap.DateField,  {
21853   
21854     template : {
21855         tag: 'div',
21856         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21857         cn: [
21858         {
21859             tag: 'div',
21860             cls: 'datepicker-days',
21861             cn: [
21862             {
21863                 tag: 'table',
21864                 cls: 'table-condensed',
21865                 cn:[
21866                 Roo.bootstrap.DateField.head,
21867                 {
21868                     tag: 'tbody'
21869                 },
21870                 Roo.bootstrap.DateField.footer
21871                 ]
21872             }
21873             ]
21874         },
21875         {
21876             tag: 'div',
21877             cls: 'datepicker-months',
21878             cn: [
21879             {
21880                 tag: 'table',
21881                 cls: 'table-condensed',
21882                 cn:[
21883                 Roo.bootstrap.DateField.head,
21884                 Roo.bootstrap.DateField.content,
21885                 Roo.bootstrap.DateField.footer
21886                 ]
21887             }
21888             ]
21889         },
21890         {
21891             tag: 'div',
21892             cls: 'datepicker-years',
21893             cn: [
21894             {
21895                 tag: 'table',
21896                 cls: 'table-condensed',
21897                 cn:[
21898                 Roo.bootstrap.DateField.head,
21899                 Roo.bootstrap.DateField.content,
21900                 Roo.bootstrap.DateField.footer
21901                 ]
21902             }
21903             ]
21904         }
21905         ]
21906     }
21907 });
21908
21909  
21910
21911  /*
21912  * - LGPL
21913  *
21914  * TimeField
21915  * 
21916  */
21917
21918 /**
21919  * @class Roo.bootstrap.TimeField
21920  * @extends Roo.bootstrap.Input
21921  * Bootstrap DateField class
21922  * 
21923  * 
21924  * @constructor
21925  * Create a new TimeField
21926  * @param {Object} config The config object
21927  */
21928
21929 Roo.bootstrap.TimeField = function(config){
21930     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21931     this.addEvents({
21932             /**
21933              * @event show
21934              * Fires when this field show.
21935              * @param {Roo.bootstrap.DateField} thisthis
21936              * @param {Mixed} date The date value
21937              */
21938             show : true,
21939             /**
21940              * @event show
21941              * Fires when this field hide.
21942              * @param {Roo.bootstrap.DateField} this
21943              * @param {Mixed} date The date value
21944              */
21945             hide : true,
21946             /**
21947              * @event select
21948              * Fires when select a date.
21949              * @param {Roo.bootstrap.DateField} this
21950              * @param {Mixed} date The date value
21951              */
21952             select : true
21953         });
21954 };
21955
21956 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21957     
21958     /**
21959      * @cfg {String} format
21960      * The default time format string which can be overriden for localization support.  The format must be
21961      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21962      */
21963     format : "H:i",
21964
21965     getAutoCreate : function()
21966     {
21967         this.after = '<i class="fa far fa-clock"></i>';
21968         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
21969         
21970          
21971     },
21972     onRender: function(ct, position)
21973     {
21974         
21975         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21976                 
21977         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
21978         
21979         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21980         
21981         this.pop = this.picker().select('>.datepicker-time',true).first();
21982         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21983         
21984         this.picker().on('mousedown', this.onMousedown, this);
21985         this.picker().on('click', this.onClick, this);
21986         
21987         this.picker().addClass('datepicker-dropdown');
21988     
21989         this.fillTime();
21990         this.update();
21991             
21992         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
21993         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
21994         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21995         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21996         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21997         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21998
21999     },
22000     
22001     fireKey: function(e){
22002         if (!this.picker().isVisible()){
22003             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22004                 this.show();
22005             }
22006             return;
22007         }
22008
22009         e.preventDefault();
22010         
22011         switch(e.keyCode){
22012             case 27: // escape
22013                 this.hide();
22014                 break;
22015             case 37: // left
22016             case 39: // right
22017                 this.onTogglePeriod();
22018                 break;
22019             case 38: // up
22020                 this.onIncrementMinutes();
22021                 break;
22022             case 40: // down
22023                 this.onDecrementMinutes();
22024                 break;
22025             case 13: // enter
22026             case 9: // tab
22027                 this.setTime();
22028                 break;
22029         }
22030     },
22031     
22032     onClick: function(e) {
22033         e.stopPropagation();
22034         e.preventDefault();
22035     },
22036     
22037     picker : function()
22038     {
22039         return this.pickerEl;
22040     },
22041     
22042     fillTime: function()
22043     {    
22044         var time = this.pop.select('tbody', true).first();
22045         
22046         time.dom.innerHTML = '';
22047         
22048         time.createChild({
22049             tag: 'tr',
22050             cn: [
22051                 {
22052                     tag: 'td',
22053                     cn: [
22054                         {
22055                             tag: 'a',
22056                             href: '#',
22057                             cls: 'btn',
22058                             cn: [
22059                                 {
22060                                     tag: 'i',
22061                                     cls: 'hours-up fa fas fa-chevron-up'
22062                                 }
22063                             ]
22064                         } 
22065                     ]
22066                 },
22067                 {
22068                     tag: 'td',
22069                     cls: 'separator'
22070                 },
22071                 {
22072                     tag: 'td',
22073                     cn: [
22074                         {
22075                             tag: 'a',
22076                             href: '#',
22077                             cls: 'btn',
22078                             cn: [
22079                                 {
22080                                     tag: 'i',
22081                                     cls: 'minutes-up fa fas fa-chevron-up'
22082                                 }
22083                             ]
22084                         }
22085                     ]
22086                 },
22087                 {
22088                     tag: 'td',
22089                     cls: 'separator'
22090                 }
22091             ]
22092         });
22093         
22094         time.createChild({
22095             tag: 'tr',
22096             cn: [
22097                 {
22098                     tag: 'td',
22099                     cn: [
22100                         {
22101                             tag: 'span',
22102                             cls: 'timepicker-hour',
22103                             html: '00'
22104                         }  
22105                     ]
22106                 },
22107                 {
22108                     tag: 'td',
22109                     cls: 'separator',
22110                     html: ':'
22111                 },
22112                 {
22113                     tag: 'td',
22114                     cn: [
22115                         {
22116                             tag: 'span',
22117                             cls: 'timepicker-minute',
22118                             html: '00'
22119                         }  
22120                     ]
22121                 },
22122                 {
22123                     tag: 'td',
22124                     cls: 'separator'
22125                 },
22126                 {
22127                     tag: 'td',
22128                     cn: [
22129                         {
22130                             tag: 'button',
22131                             type: 'button',
22132                             cls: 'btn btn-primary period',
22133                             html: 'AM'
22134                             
22135                         }
22136                     ]
22137                 }
22138             ]
22139         });
22140         
22141         time.createChild({
22142             tag: 'tr',
22143             cn: [
22144                 {
22145                     tag: 'td',
22146                     cn: [
22147                         {
22148                             tag: 'a',
22149                             href: '#',
22150                             cls: 'btn',
22151                             cn: [
22152                                 {
22153                                     tag: 'span',
22154                                     cls: 'hours-down fa fas fa-chevron-down'
22155                                 }
22156                             ]
22157                         }
22158                     ]
22159                 },
22160                 {
22161                     tag: 'td',
22162                     cls: 'separator'
22163                 },
22164                 {
22165                     tag: 'td',
22166                     cn: [
22167                         {
22168                             tag: 'a',
22169                             href: '#',
22170                             cls: 'btn',
22171                             cn: [
22172                                 {
22173                                     tag: 'span',
22174                                     cls: 'minutes-down fa fas fa-chevron-down'
22175                                 }
22176                             ]
22177                         }
22178                     ]
22179                 },
22180                 {
22181                     tag: 'td',
22182                     cls: 'separator'
22183                 }
22184             ]
22185         });
22186         
22187     },
22188     
22189     update: function()
22190     {
22191         
22192         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22193         
22194         this.fill();
22195     },
22196     
22197     fill: function() 
22198     {
22199         var hours = this.time.getHours();
22200         var minutes = this.time.getMinutes();
22201         var period = 'AM';
22202         
22203         if(hours > 11){
22204             period = 'PM';
22205         }
22206         
22207         if(hours == 0){
22208             hours = 12;
22209         }
22210         
22211         
22212         if(hours > 12){
22213             hours = hours - 12;
22214         }
22215         
22216         if(hours < 10){
22217             hours = '0' + hours;
22218         }
22219         
22220         if(minutes < 10){
22221             minutes = '0' + minutes;
22222         }
22223         
22224         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22225         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22226         this.pop.select('button', true).first().dom.innerHTML = period;
22227         
22228     },
22229     
22230     place: function()
22231     {   
22232         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22233         
22234         var cls = ['bottom'];
22235         
22236         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22237             cls.pop();
22238             cls.push('top');
22239         }
22240         
22241         cls.push('right');
22242         
22243         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22244             cls.pop();
22245             cls.push('left');
22246         }
22247         //this.picker().setXY(20000,20000);
22248         this.picker().addClass(cls.join('-'));
22249         
22250         var _this = this;
22251         
22252         Roo.each(cls, function(c){
22253             if(c == 'bottom'){
22254                 (function() {
22255                  //  
22256                 }).defer(200);
22257                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22258                 //_this.picker().setTop(_this.inputEl().getHeight());
22259                 return;
22260             }
22261             if(c == 'top'){
22262                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22263                 
22264                 //_this.picker().setTop(0 - _this.picker().getHeight());
22265                 return;
22266             }
22267             /*
22268             if(c == 'left'){
22269                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22270                 return;
22271             }
22272             if(c == 'right'){
22273                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22274                 return;
22275             }
22276             */
22277         });
22278         
22279     },
22280   
22281     onFocus : function()
22282     {
22283         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22284         this.show();
22285     },
22286     
22287     onBlur : function()
22288     {
22289         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22290         this.hide();
22291     },
22292     
22293     show : function()
22294     {
22295         this.picker().show();
22296         this.pop.show();
22297         this.update();
22298         this.place();
22299         
22300         this.fireEvent('show', this, this.date);
22301     },
22302     
22303     hide : function()
22304     {
22305         this.picker().hide();
22306         this.pop.hide();
22307         
22308         this.fireEvent('hide', this, this.date);
22309     },
22310     
22311     setTime : function()
22312     {
22313         this.hide();
22314         this.setValue(this.time.format(this.format));
22315         
22316         this.fireEvent('select', this, this.date);
22317         
22318         
22319     },
22320     
22321     onMousedown: function(e){
22322         e.stopPropagation();
22323         e.preventDefault();
22324     },
22325     
22326     onIncrementHours: function()
22327     {
22328         Roo.log('onIncrementHours');
22329         this.time = this.time.add(Date.HOUR, 1);
22330         this.update();
22331         
22332     },
22333     
22334     onDecrementHours: function()
22335     {
22336         Roo.log('onDecrementHours');
22337         this.time = this.time.add(Date.HOUR, -1);
22338         this.update();
22339     },
22340     
22341     onIncrementMinutes: function()
22342     {
22343         Roo.log('onIncrementMinutes');
22344         this.time = this.time.add(Date.MINUTE, 1);
22345         this.update();
22346     },
22347     
22348     onDecrementMinutes: function()
22349     {
22350         Roo.log('onDecrementMinutes');
22351         this.time = this.time.add(Date.MINUTE, -1);
22352         this.update();
22353     },
22354     
22355     onTogglePeriod: function()
22356     {
22357         Roo.log('onTogglePeriod');
22358         this.time = this.time.add(Date.HOUR, 12);
22359         this.update();
22360     }
22361     
22362    
22363 });
22364  
22365
22366 Roo.apply(Roo.bootstrap.TimeField,  {
22367   
22368     template : {
22369         tag: 'div',
22370         cls: 'datepicker dropdown-menu',
22371         cn: [
22372             {
22373                 tag: 'div',
22374                 cls: 'datepicker-time',
22375                 cn: [
22376                 {
22377                     tag: 'table',
22378                     cls: 'table-condensed',
22379                     cn:[
22380                         {
22381                             tag: 'tbody',
22382                             cn: [
22383                                 {
22384                                     tag: 'tr',
22385                                     cn: [
22386                                     {
22387                                         tag: 'td',
22388                                         colspan: '7'
22389                                     }
22390                                     ]
22391                                 }
22392                             ]
22393                         },
22394                         {
22395                             tag: 'tfoot',
22396                             cn: [
22397                                 {
22398                                     tag: 'tr',
22399                                     cn: [
22400                                     {
22401                                         tag: 'th',
22402                                         colspan: '7',
22403                                         cls: '',
22404                                         cn: [
22405                                             {
22406                                                 tag: 'button',
22407                                                 cls: 'btn btn-info ok',
22408                                                 html: 'OK'
22409                                             }
22410                                         ]
22411                                     }
22412                     
22413                                     ]
22414                                 }
22415                             ]
22416                         }
22417                     ]
22418                 }
22419                 ]
22420             }
22421         ]
22422     }
22423 });
22424
22425  
22426
22427  /*
22428  * - LGPL
22429  *
22430  * MonthField
22431  * 
22432  */
22433
22434 /**
22435  * @class Roo.bootstrap.MonthField
22436  * @extends Roo.bootstrap.Input
22437  * Bootstrap MonthField class
22438  * 
22439  * @cfg {String} language default en
22440  * 
22441  * @constructor
22442  * Create a new MonthField
22443  * @param {Object} config The config object
22444  */
22445
22446 Roo.bootstrap.MonthField = function(config){
22447     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22448     
22449     this.addEvents({
22450         /**
22451          * @event show
22452          * Fires when this field show.
22453          * @param {Roo.bootstrap.MonthField} this
22454          * @param {Mixed} date The date value
22455          */
22456         show : true,
22457         /**
22458          * @event show
22459          * Fires when this field hide.
22460          * @param {Roo.bootstrap.MonthField} this
22461          * @param {Mixed} date The date value
22462          */
22463         hide : true,
22464         /**
22465          * @event select
22466          * Fires when select a date.
22467          * @param {Roo.bootstrap.MonthField} this
22468          * @param {String} oldvalue The old value
22469          * @param {String} newvalue The new value
22470          */
22471         select : true
22472     });
22473 };
22474
22475 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22476     
22477     onRender: function(ct, position)
22478     {
22479         
22480         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22481         
22482         this.language = this.language || 'en';
22483         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22484         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22485         
22486         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22487         this.isInline = false;
22488         this.isInput = true;
22489         this.component = this.el.select('.add-on', true).first() || false;
22490         this.component = (this.component && this.component.length === 0) ? false : this.component;
22491         this.hasInput = this.component && this.inputEL().length;
22492         
22493         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22494         
22495         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22496         
22497         this.picker().on('mousedown', this.onMousedown, this);
22498         this.picker().on('click', this.onClick, this);
22499         
22500         this.picker().addClass('datepicker-dropdown');
22501         
22502         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22503             v.setStyle('width', '189px');
22504         });
22505         
22506         this.fillMonths();
22507         
22508         this.update();
22509         
22510         if(this.isInline) {
22511             this.show();
22512         }
22513         
22514     },
22515     
22516     setValue: function(v, suppressEvent)
22517     {   
22518         var o = this.getValue();
22519         
22520         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22521         
22522         this.update();
22523
22524         if(suppressEvent !== true){
22525             this.fireEvent('select', this, o, v);
22526         }
22527         
22528     },
22529     
22530     getValue: function()
22531     {
22532         return this.value;
22533     },
22534     
22535     onClick: function(e) 
22536     {
22537         e.stopPropagation();
22538         e.preventDefault();
22539         
22540         var target = e.getTarget();
22541         
22542         if(target.nodeName.toLowerCase() === 'i'){
22543             target = Roo.get(target).dom.parentNode;
22544         }
22545         
22546         var nodeName = target.nodeName;
22547         var className = target.className;
22548         var html = target.innerHTML;
22549         
22550         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22551             return;
22552         }
22553         
22554         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22555         
22556         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22557         
22558         this.hide();
22559                         
22560     },
22561     
22562     picker : function()
22563     {
22564         return this.pickerEl;
22565     },
22566     
22567     fillMonths: function()
22568     {    
22569         var i = 0;
22570         var months = this.picker().select('>.datepicker-months td', true).first();
22571         
22572         months.dom.innerHTML = '';
22573         
22574         while (i < 12) {
22575             var month = {
22576                 tag: 'span',
22577                 cls: 'month',
22578                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22579             };
22580             
22581             months.createChild(month);
22582         }
22583         
22584     },
22585     
22586     update: function()
22587     {
22588         var _this = this;
22589         
22590         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22591             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22592         }
22593         
22594         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22595             e.removeClass('active');
22596             
22597             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22598                 e.addClass('active');
22599             }
22600         })
22601     },
22602     
22603     place: function()
22604     {
22605         if(this.isInline) {
22606             return;
22607         }
22608         
22609         this.picker().removeClass(['bottom', 'top']);
22610         
22611         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22612             /*
22613              * place to the top of element!
22614              *
22615              */
22616             
22617             this.picker().addClass('top');
22618             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22619             
22620             return;
22621         }
22622         
22623         this.picker().addClass('bottom');
22624         
22625         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22626     },
22627     
22628     onFocus : function()
22629     {
22630         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22631         this.show();
22632     },
22633     
22634     onBlur : function()
22635     {
22636         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22637         
22638         var d = this.inputEl().getValue();
22639         
22640         this.setValue(d);
22641                 
22642         this.hide();
22643     },
22644     
22645     show : function()
22646     {
22647         this.picker().show();
22648         this.picker().select('>.datepicker-months', true).first().show();
22649         this.update();
22650         this.place();
22651         
22652         this.fireEvent('show', this, this.date);
22653     },
22654     
22655     hide : function()
22656     {
22657         if(this.isInline) {
22658             return;
22659         }
22660         this.picker().hide();
22661         this.fireEvent('hide', this, this.date);
22662         
22663     },
22664     
22665     onMousedown: function(e)
22666     {
22667         e.stopPropagation();
22668         e.preventDefault();
22669     },
22670     
22671     keyup: function(e)
22672     {
22673         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22674         this.update();
22675     },
22676
22677     fireKey: function(e)
22678     {
22679         if (!this.picker().isVisible()){
22680             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22681                 this.show();
22682             }
22683             return;
22684         }
22685         
22686         var dir;
22687         
22688         switch(e.keyCode){
22689             case 27: // escape
22690                 this.hide();
22691                 e.preventDefault();
22692                 break;
22693             case 37: // left
22694             case 39: // right
22695                 dir = e.keyCode == 37 ? -1 : 1;
22696                 
22697                 this.vIndex = this.vIndex + dir;
22698                 
22699                 if(this.vIndex < 0){
22700                     this.vIndex = 0;
22701                 }
22702                 
22703                 if(this.vIndex > 11){
22704                     this.vIndex = 11;
22705                 }
22706                 
22707                 if(isNaN(this.vIndex)){
22708                     this.vIndex = 0;
22709                 }
22710                 
22711                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22712                 
22713                 break;
22714             case 38: // up
22715             case 40: // down
22716                 
22717                 dir = e.keyCode == 38 ? -1 : 1;
22718                 
22719                 this.vIndex = this.vIndex + dir * 4;
22720                 
22721                 if(this.vIndex < 0){
22722                     this.vIndex = 0;
22723                 }
22724                 
22725                 if(this.vIndex > 11){
22726                     this.vIndex = 11;
22727                 }
22728                 
22729                 if(isNaN(this.vIndex)){
22730                     this.vIndex = 0;
22731                 }
22732                 
22733                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22734                 break;
22735                 
22736             case 13: // enter
22737                 
22738                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22739                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22740                 }
22741                 
22742                 this.hide();
22743                 e.preventDefault();
22744                 break;
22745             case 9: // tab
22746                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22747                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22748                 }
22749                 this.hide();
22750                 break;
22751             case 16: // shift
22752             case 17: // ctrl
22753             case 18: // alt
22754                 break;
22755             default :
22756                 this.hide();
22757                 
22758         }
22759     },
22760     
22761     remove: function() 
22762     {
22763         this.picker().remove();
22764     }
22765    
22766 });
22767
22768 Roo.apply(Roo.bootstrap.MonthField,  {
22769     
22770     content : {
22771         tag: 'tbody',
22772         cn: [
22773         {
22774             tag: 'tr',
22775             cn: [
22776             {
22777                 tag: 'td',
22778                 colspan: '7'
22779             }
22780             ]
22781         }
22782         ]
22783     },
22784     
22785     dates:{
22786         en: {
22787             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22788             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22789         }
22790     }
22791 });
22792
22793 Roo.apply(Roo.bootstrap.MonthField,  {
22794   
22795     template : {
22796         tag: 'div',
22797         cls: 'datepicker dropdown-menu roo-dynamic',
22798         cn: [
22799             {
22800                 tag: 'div',
22801                 cls: 'datepicker-months',
22802                 cn: [
22803                 {
22804                     tag: 'table',
22805                     cls: 'table-condensed',
22806                     cn:[
22807                         Roo.bootstrap.DateField.content
22808                     ]
22809                 }
22810                 ]
22811             }
22812         ]
22813     }
22814 });
22815
22816  
22817
22818  
22819  /*
22820  * - LGPL
22821  *
22822  * CheckBox
22823  * 
22824  */
22825
22826 /**
22827  * @class Roo.bootstrap.CheckBox
22828  * @extends Roo.bootstrap.Input
22829  * Bootstrap CheckBox class
22830  * 
22831  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22832  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22833  * @cfg {String} boxLabel The text that appears beside the checkbox
22834  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22835  * @cfg {Boolean} checked initnal the element
22836  * @cfg {Boolean} inline inline the element (default false)
22837  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22838  * @cfg {String} tooltip label tooltip
22839  * 
22840  * @constructor
22841  * Create a new CheckBox
22842  * @param {Object} config The config object
22843  */
22844
22845 Roo.bootstrap.CheckBox = function(config){
22846     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22847    
22848     this.addEvents({
22849         /**
22850         * @event check
22851         * Fires when the element is checked or unchecked.
22852         * @param {Roo.bootstrap.CheckBox} this This input
22853         * @param {Boolean} checked The new checked value
22854         */
22855        check : true,
22856        /**
22857         * @event click
22858         * Fires when the element is click.
22859         * @param {Roo.bootstrap.CheckBox} this This input
22860         */
22861        click : true
22862     });
22863     
22864 };
22865
22866 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22867   
22868     inputType: 'checkbox',
22869     inputValue: 1,
22870     valueOff: 0,
22871     boxLabel: false,
22872     checked: false,
22873     weight : false,
22874     inline: false,
22875     tooltip : '',
22876     
22877     // checkbox success does not make any sense really.. 
22878     invalidClass : "",
22879     validClass : "",
22880     
22881     
22882     getAutoCreate : function()
22883     {
22884         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22885         
22886         var id = Roo.id();
22887         
22888         var cfg = {};
22889         
22890         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22891         
22892         if(this.inline){
22893             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22894         }
22895         
22896         var input =  {
22897             tag: 'input',
22898             id : id,
22899             type : this.inputType,
22900             value : this.inputValue,
22901             cls : 'roo-' + this.inputType, //'form-box',
22902             placeholder : this.placeholder || ''
22903             
22904         };
22905         
22906         if(this.inputType != 'radio'){
22907             var hidden =  {
22908                 tag: 'input',
22909                 type : 'hidden',
22910                 cls : 'roo-hidden-value',
22911                 value : this.checked ? this.inputValue : this.valueOff
22912             };
22913         }
22914         
22915             
22916         if (this.weight) { // Validity check?
22917             cfg.cls += " " + this.inputType + "-" + this.weight;
22918         }
22919         
22920         if (this.disabled) {
22921             input.disabled=true;
22922         }
22923         
22924         if(this.checked){
22925             input.checked = this.checked;
22926         }
22927         
22928         if (this.name) {
22929             
22930             input.name = this.name;
22931             
22932             if(this.inputType != 'radio'){
22933                 hidden.name = this.name;
22934                 input.name = '_hidden_' + this.name;
22935             }
22936         }
22937         
22938         if (this.size) {
22939             input.cls += ' input-' + this.size;
22940         }
22941         
22942         var settings=this;
22943         
22944         ['xs','sm','md','lg'].map(function(size){
22945             if (settings[size]) {
22946                 cfg.cls += ' col-' + size + '-' + settings[size];
22947             }
22948         });
22949         
22950         var inputblock = input;
22951          
22952         if (this.before || this.after) {
22953             
22954             inputblock = {
22955                 cls : 'input-group',
22956                 cn :  [] 
22957             };
22958             
22959             if (this.before) {
22960                 inputblock.cn.push({
22961                     tag :'span',
22962                     cls : 'input-group-addon',
22963                     html : this.before
22964                 });
22965             }
22966             
22967             inputblock.cn.push(input);
22968             
22969             if(this.inputType != 'radio'){
22970                 inputblock.cn.push(hidden);
22971             }
22972             
22973             if (this.after) {
22974                 inputblock.cn.push({
22975                     tag :'span',
22976                     cls : 'input-group-addon',
22977                     html : this.after
22978                 });
22979             }
22980             
22981         }
22982         var boxLabelCfg = false;
22983         
22984         if(this.boxLabel){
22985            
22986             boxLabelCfg = {
22987                 tag: 'label',
22988                 //'for': id, // box label is handled by onclick - so no for...
22989                 cls: 'box-label',
22990                 html: this.boxLabel
22991             };
22992             if(this.tooltip){
22993                 boxLabelCfg.tooltip = this.tooltip;
22994             }
22995              
22996         }
22997         
22998         
22999         if (align ==='left' && this.fieldLabel.length) {
23000 //                Roo.log("left and has label");
23001             cfg.cn = [
23002                 {
23003                     tag: 'label',
23004                     'for' :  id,
23005                     cls : 'control-label',
23006                     html : this.fieldLabel
23007                 },
23008                 {
23009                     cls : "", 
23010                     cn: [
23011                         inputblock
23012                     ]
23013                 }
23014             ];
23015             
23016             if (boxLabelCfg) {
23017                 cfg.cn[1].cn.push(boxLabelCfg);
23018             }
23019             
23020             if(this.labelWidth > 12){
23021                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23022             }
23023             
23024             if(this.labelWidth < 13 && this.labelmd == 0){
23025                 this.labelmd = this.labelWidth;
23026             }
23027             
23028             if(this.labellg > 0){
23029                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23030                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23031             }
23032             
23033             if(this.labelmd > 0){
23034                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23035                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23036             }
23037             
23038             if(this.labelsm > 0){
23039                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23040                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23041             }
23042             
23043             if(this.labelxs > 0){
23044                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23045                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23046             }
23047             
23048         } else if ( this.fieldLabel.length) {
23049 //                Roo.log(" label");
23050                 cfg.cn = [
23051                    
23052                     {
23053                         tag: this.boxLabel ? 'span' : 'label',
23054                         'for': id,
23055                         cls: 'control-label box-input-label',
23056                         //cls : 'input-group-addon',
23057                         html : this.fieldLabel
23058                     },
23059                     
23060                     inputblock
23061                     
23062                 ];
23063                 if (boxLabelCfg) {
23064                     cfg.cn.push(boxLabelCfg);
23065                 }
23066
23067         } else {
23068             
23069 //                Roo.log(" no label && no align");
23070                 cfg.cn = [  inputblock ] ;
23071                 if (boxLabelCfg) {
23072                     cfg.cn.push(boxLabelCfg);
23073                 }
23074
23075                 
23076         }
23077         
23078        
23079         
23080         if(this.inputType != 'radio'){
23081             cfg.cn.push(hidden);
23082         }
23083         
23084         return cfg;
23085         
23086     },
23087     
23088     /**
23089      * return the real input element.
23090      */
23091     inputEl: function ()
23092     {
23093         return this.el.select('input.roo-' + this.inputType,true).first();
23094     },
23095     hiddenEl: function ()
23096     {
23097         return this.el.select('input.roo-hidden-value',true).first();
23098     },
23099     
23100     labelEl: function()
23101     {
23102         return this.el.select('label.control-label',true).first();
23103     },
23104     /* depricated... */
23105     
23106     label: function()
23107     {
23108         return this.labelEl();
23109     },
23110     
23111     boxLabelEl: function()
23112     {
23113         return this.el.select('label.box-label',true).first();
23114     },
23115     
23116     initEvents : function()
23117     {
23118 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23119         
23120         this.inputEl().on('click', this.onClick,  this);
23121         
23122         if (this.boxLabel) { 
23123             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23124         }
23125         
23126         this.startValue = this.getValue();
23127         
23128         if(this.groupId){
23129             Roo.bootstrap.CheckBox.register(this);
23130         }
23131     },
23132     
23133     onClick : function(e)
23134     {   
23135         if(this.fireEvent('click', this, e) !== false){
23136             this.setChecked(!this.checked);
23137         }
23138         
23139     },
23140     
23141     setChecked : function(state,suppressEvent)
23142     {
23143         this.startValue = this.getValue();
23144
23145         if(this.inputType == 'radio'){
23146             
23147             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23148                 e.dom.checked = false;
23149             });
23150             
23151             this.inputEl().dom.checked = true;
23152             
23153             this.inputEl().dom.value = this.inputValue;
23154             
23155             if(suppressEvent !== true){
23156                 this.fireEvent('check', this, true);
23157             }
23158             
23159             this.validate();
23160             
23161             return;
23162         }
23163         
23164         this.checked = state;
23165         
23166         this.inputEl().dom.checked = state;
23167         
23168         
23169         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23170         
23171         if(suppressEvent !== true){
23172             this.fireEvent('check', this, state);
23173         }
23174         
23175         this.validate();
23176     },
23177     
23178     getValue : function()
23179     {
23180         if(this.inputType == 'radio'){
23181             return this.getGroupValue();
23182         }
23183         
23184         return this.hiddenEl().dom.value;
23185         
23186     },
23187     
23188     getGroupValue : function()
23189     {
23190         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23191             return '';
23192         }
23193         
23194         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23195     },
23196     
23197     setValue : function(v,suppressEvent)
23198     {
23199         if(this.inputType == 'radio'){
23200             this.setGroupValue(v, suppressEvent);
23201             return;
23202         }
23203         
23204         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23205         
23206         this.validate();
23207     },
23208     
23209     setGroupValue : function(v, suppressEvent)
23210     {
23211         this.startValue = this.getValue();
23212         
23213         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23214             e.dom.checked = false;
23215             
23216             if(e.dom.value == v){
23217                 e.dom.checked = true;
23218             }
23219         });
23220         
23221         if(suppressEvent !== true){
23222             this.fireEvent('check', this, true);
23223         }
23224
23225         this.validate();
23226         
23227         return;
23228     },
23229     
23230     validate : function()
23231     {
23232         if(this.getVisibilityEl().hasClass('hidden')){
23233             return true;
23234         }
23235         
23236         if(
23237                 this.disabled || 
23238                 (this.inputType == 'radio' && this.validateRadio()) ||
23239                 (this.inputType == 'checkbox' && this.validateCheckbox())
23240         ){
23241             this.markValid();
23242             return true;
23243         }
23244         
23245         this.markInvalid();
23246         return false;
23247     },
23248     
23249     validateRadio : function()
23250     {
23251         if(this.getVisibilityEl().hasClass('hidden')){
23252             return true;
23253         }
23254         
23255         if(this.allowBlank){
23256             return true;
23257         }
23258         
23259         var valid = false;
23260         
23261         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23262             if(!e.dom.checked){
23263                 return;
23264             }
23265             
23266             valid = true;
23267             
23268             return false;
23269         });
23270         
23271         return valid;
23272     },
23273     
23274     validateCheckbox : function()
23275     {
23276         if(!this.groupId){
23277             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23278             //return (this.getValue() == this.inputValue) ? true : false;
23279         }
23280         
23281         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23282         
23283         if(!group){
23284             return false;
23285         }
23286         
23287         var r = false;
23288         
23289         for(var i in group){
23290             if(group[i].el.isVisible(true)){
23291                 r = false;
23292                 break;
23293             }
23294             
23295             r = true;
23296         }
23297         
23298         for(var i in group){
23299             if(r){
23300                 break;
23301             }
23302             
23303             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23304         }
23305         
23306         return r;
23307     },
23308     
23309     /**
23310      * Mark this field as valid
23311      */
23312     markValid : function()
23313     {
23314         var _this = this;
23315         
23316         this.fireEvent('valid', this);
23317         
23318         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23319         
23320         if(this.groupId){
23321             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23322         }
23323         
23324         if(label){
23325             label.markValid();
23326         }
23327
23328         if(this.inputType == 'radio'){
23329             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23330                 var fg = e.findParent('.form-group', false, true);
23331                 if (Roo.bootstrap.version == 3) {
23332                     fg.removeClass([_this.invalidClass, _this.validClass]);
23333                     fg.addClass(_this.validClass);
23334                 } else {
23335                     fg.removeClass(['is-valid', 'is-invalid']);
23336                     fg.addClass('is-valid');
23337                 }
23338             });
23339             
23340             return;
23341         }
23342
23343         if(!this.groupId){
23344             var fg = this.el.findParent('.form-group', false, true);
23345             if (Roo.bootstrap.version == 3) {
23346                 fg.removeClass([this.invalidClass, this.validClass]);
23347                 fg.addClass(this.validClass);
23348             } else {
23349                 fg.removeClass(['is-valid', 'is-invalid']);
23350                 fg.addClass('is-valid');
23351             }
23352             return;
23353         }
23354         
23355         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23356         
23357         if(!group){
23358             return;
23359         }
23360         
23361         for(var i in group){
23362             var fg = group[i].el.findParent('.form-group', false, true);
23363             if (Roo.bootstrap.version == 3) {
23364                 fg.removeClass([this.invalidClass, this.validClass]);
23365                 fg.addClass(this.validClass);
23366             } else {
23367                 fg.removeClass(['is-valid', 'is-invalid']);
23368                 fg.addClass('is-valid');
23369             }
23370         }
23371     },
23372     
23373      /**
23374      * Mark this field as invalid
23375      * @param {String} msg The validation message
23376      */
23377     markInvalid : function(msg)
23378     {
23379         if(this.allowBlank){
23380             return;
23381         }
23382         
23383         var _this = this;
23384         
23385         this.fireEvent('invalid', this, msg);
23386         
23387         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23388         
23389         if(this.groupId){
23390             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23391         }
23392         
23393         if(label){
23394             label.markInvalid();
23395         }
23396             
23397         if(this.inputType == 'radio'){
23398             
23399             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23400                 var fg = e.findParent('.form-group', false, true);
23401                 if (Roo.bootstrap.version == 3) {
23402                     fg.removeClass([_this.invalidClass, _this.validClass]);
23403                     fg.addClass(_this.invalidClass);
23404                 } else {
23405                     fg.removeClass(['is-invalid', 'is-valid']);
23406                     fg.addClass('is-invalid');
23407                 }
23408             });
23409             
23410             return;
23411         }
23412         
23413         if(!this.groupId){
23414             var fg = this.el.findParent('.form-group', false, true);
23415             if (Roo.bootstrap.version == 3) {
23416                 fg.removeClass([_this.invalidClass, _this.validClass]);
23417                 fg.addClass(_this.invalidClass);
23418             } else {
23419                 fg.removeClass(['is-invalid', 'is-valid']);
23420                 fg.addClass('is-invalid');
23421             }
23422             return;
23423         }
23424         
23425         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23426         
23427         if(!group){
23428             return;
23429         }
23430         
23431         for(var i in group){
23432             var fg = group[i].el.findParent('.form-group', false, true);
23433             if (Roo.bootstrap.version == 3) {
23434                 fg.removeClass([_this.invalidClass, _this.validClass]);
23435                 fg.addClass(_this.invalidClass);
23436             } else {
23437                 fg.removeClass(['is-invalid', 'is-valid']);
23438                 fg.addClass('is-invalid');
23439             }
23440         }
23441         
23442     },
23443     
23444     clearInvalid : function()
23445     {
23446         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23447         
23448         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23449         
23450         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23451         
23452         if (label && label.iconEl) {
23453             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23454             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23455         }
23456     },
23457     
23458     disable : function()
23459     {
23460         if(this.inputType != 'radio'){
23461             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23462             return;
23463         }
23464         
23465         var _this = this;
23466         
23467         if(this.rendered){
23468             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23469                 _this.getActionEl().addClass(this.disabledClass);
23470                 e.dom.disabled = true;
23471             });
23472         }
23473         
23474         this.disabled = true;
23475         this.fireEvent("disable", this);
23476         return this;
23477     },
23478
23479     enable : function()
23480     {
23481         if(this.inputType != 'radio'){
23482             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23483             return;
23484         }
23485         
23486         var _this = this;
23487         
23488         if(this.rendered){
23489             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23490                 _this.getActionEl().removeClass(this.disabledClass);
23491                 e.dom.disabled = false;
23492             });
23493         }
23494         
23495         this.disabled = false;
23496         this.fireEvent("enable", this);
23497         return this;
23498     },
23499     
23500     setBoxLabel : function(v)
23501     {
23502         this.boxLabel = v;
23503         
23504         if(this.rendered){
23505             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23506         }
23507     }
23508
23509 });
23510
23511 Roo.apply(Roo.bootstrap.CheckBox, {
23512     
23513     groups: {},
23514     
23515      /**
23516     * register a CheckBox Group
23517     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23518     */
23519     register : function(checkbox)
23520     {
23521         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23522             this.groups[checkbox.groupId] = {};
23523         }
23524         
23525         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23526             return;
23527         }
23528         
23529         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23530         
23531     },
23532     /**
23533     * fetch a CheckBox Group based on the group ID
23534     * @param {string} the group ID
23535     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23536     */
23537     get: function(groupId) {
23538         if (typeof(this.groups[groupId]) == 'undefined') {
23539             return false;
23540         }
23541         
23542         return this.groups[groupId] ;
23543     }
23544     
23545     
23546 });
23547 /*
23548  * - LGPL
23549  *
23550  * RadioItem
23551  * 
23552  */
23553
23554 /**
23555  * @class Roo.bootstrap.Radio
23556  * @extends Roo.bootstrap.Component
23557  * Bootstrap Radio class
23558  * @cfg {String} boxLabel - the label associated
23559  * @cfg {String} value - the value of radio
23560  * 
23561  * @constructor
23562  * Create a new Radio
23563  * @param {Object} config The config object
23564  */
23565 Roo.bootstrap.Radio = function(config){
23566     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23567     
23568 };
23569
23570 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23571     
23572     boxLabel : '',
23573     
23574     value : '',
23575     
23576     getAutoCreate : function()
23577     {
23578         var cfg = {
23579             tag : 'div',
23580             cls : 'form-group radio',
23581             cn : [
23582                 {
23583                     tag : 'label',
23584                     cls : 'box-label',
23585                     html : this.boxLabel
23586                 }
23587             ]
23588         };
23589         
23590         return cfg;
23591     },
23592     
23593     initEvents : function() 
23594     {
23595         this.parent().register(this);
23596         
23597         this.el.on('click', this.onClick, this);
23598         
23599     },
23600     
23601     onClick : function(e)
23602     {
23603         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23604             this.setChecked(true);
23605         }
23606     },
23607     
23608     setChecked : function(state, suppressEvent)
23609     {
23610         this.parent().setValue(this.value, suppressEvent);
23611         
23612     },
23613     
23614     setBoxLabel : function(v)
23615     {
23616         this.boxLabel = v;
23617         
23618         if(this.rendered){
23619             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23620         }
23621     }
23622     
23623 });
23624  
23625
23626  /*
23627  * - LGPL
23628  *
23629  * Input
23630  * 
23631  */
23632
23633 /**
23634  * @class Roo.bootstrap.SecurePass
23635  * @extends Roo.bootstrap.Input
23636  * Bootstrap SecurePass class
23637  *
23638  * 
23639  * @constructor
23640  * Create a new SecurePass
23641  * @param {Object} config The config object
23642  */
23643  
23644 Roo.bootstrap.SecurePass = function (config) {
23645     // these go here, so the translation tool can replace them..
23646     this.errors = {
23647         PwdEmpty: "Please type a password, and then retype it to confirm.",
23648         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23649         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23650         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23651         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23652         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23653         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23654         TooWeak: "Your password is Too Weak."
23655     },
23656     this.meterLabel = "Password strength:";
23657     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23658     this.meterClass = [
23659         "roo-password-meter-tooweak", 
23660         "roo-password-meter-weak", 
23661         "roo-password-meter-medium", 
23662         "roo-password-meter-strong", 
23663         "roo-password-meter-grey"
23664     ];
23665     
23666     this.errors = {};
23667     
23668     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23669 }
23670
23671 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23672     /**
23673      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23674      * {
23675      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23676      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23677      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23678      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23679      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23680      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23681      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23682      * })
23683      */
23684     // private
23685     
23686     meterWidth: 300,
23687     errorMsg :'',    
23688     errors: false,
23689     imageRoot: '/',
23690     /**
23691      * @cfg {String/Object} Label for the strength meter (defaults to
23692      * 'Password strength:')
23693      */
23694     // private
23695     meterLabel: '',
23696     /**
23697      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23698      * ['Weak', 'Medium', 'Strong'])
23699      */
23700     // private    
23701     pwdStrengths: false,    
23702     // private
23703     strength: 0,
23704     // private
23705     _lastPwd: null,
23706     // private
23707     kCapitalLetter: 0,
23708     kSmallLetter: 1,
23709     kDigit: 2,
23710     kPunctuation: 3,
23711     
23712     insecure: false,
23713     // private
23714     initEvents: function ()
23715     {
23716         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23717
23718         if (this.el.is('input[type=password]') && Roo.isSafari) {
23719             this.el.on('keydown', this.SafariOnKeyDown, this);
23720         }
23721
23722         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23723     },
23724     // private
23725     onRender: function (ct, position)
23726     {
23727         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23728         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23729         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23730
23731         this.trigger.createChild({
23732                    cn: [
23733                     {
23734                     //id: 'PwdMeter',
23735                     tag: 'div',
23736                     cls: 'roo-password-meter-grey col-xs-12',
23737                     style: {
23738                         //width: 0,
23739                         //width: this.meterWidth + 'px'                                                
23740                         }
23741                     },
23742                     {                            
23743                          cls: 'roo-password-meter-text'                          
23744                     }
23745                 ]            
23746         });
23747
23748          
23749         if (this.hideTrigger) {
23750             this.trigger.setDisplayed(false);
23751         }
23752         this.setSize(this.width || '', this.height || '');
23753     },
23754     // private
23755     onDestroy: function ()
23756     {
23757         if (this.trigger) {
23758             this.trigger.removeAllListeners();
23759             this.trigger.remove();
23760         }
23761         if (this.wrap) {
23762             this.wrap.remove();
23763         }
23764         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23765     },
23766     // private
23767     checkStrength: function ()
23768     {
23769         var pwd = this.inputEl().getValue();
23770         if (pwd == this._lastPwd) {
23771             return;
23772         }
23773
23774         var strength;
23775         if (this.ClientSideStrongPassword(pwd)) {
23776             strength = 3;
23777         } else if (this.ClientSideMediumPassword(pwd)) {
23778             strength = 2;
23779         } else if (this.ClientSideWeakPassword(pwd)) {
23780             strength = 1;
23781         } else {
23782             strength = 0;
23783         }
23784         
23785         Roo.log('strength1: ' + strength);
23786         
23787         //var pm = this.trigger.child('div/div/div').dom;
23788         var pm = this.trigger.child('div/div');
23789         pm.removeClass(this.meterClass);
23790         pm.addClass(this.meterClass[strength]);
23791                 
23792         
23793         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23794                 
23795         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23796         
23797         this._lastPwd = pwd;
23798     },
23799     reset: function ()
23800     {
23801         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23802         
23803         this._lastPwd = '';
23804         
23805         var pm = this.trigger.child('div/div');
23806         pm.removeClass(this.meterClass);
23807         pm.addClass('roo-password-meter-grey');        
23808         
23809         
23810         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23811         
23812         pt.innerHTML = '';
23813         this.inputEl().dom.type='password';
23814     },
23815     // private
23816     validateValue: function (value)
23817     {
23818         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23819             return false;
23820         }
23821         if (value.length == 0) {
23822             if (this.allowBlank) {
23823                 this.clearInvalid();
23824                 return true;
23825             }
23826
23827             this.markInvalid(this.errors.PwdEmpty);
23828             this.errorMsg = this.errors.PwdEmpty;
23829             return false;
23830         }
23831         
23832         if(this.insecure){
23833             return true;
23834         }
23835         
23836         if (!value.match(/[\x21-\x7e]+/)) {
23837             this.markInvalid(this.errors.PwdBadChar);
23838             this.errorMsg = this.errors.PwdBadChar;
23839             return false;
23840         }
23841         if (value.length < 6) {
23842             this.markInvalid(this.errors.PwdShort);
23843             this.errorMsg = this.errors.PwdShort;
23844             return false;
23845         }
23846         if (value.length > 16) {
23847             this.markInvalid(this.errors.PwdLong);
23848             this.errorMsg = this.errors.PwdLong;
23849             return false;
23850         }
23851         var strength;
23852         if (this.ClientSideStrongPassword(value)) {
23853             strength = 3;
23854         } else if (this.ClientSideMediumPassword(value)) {
23855             strength = 2;
23856         } else if (this.ClientSideWeakPassword(value)) {
23857             strength = 1;
23858         } else {
23859             strength = 0;
23860         }
23861
23862         
23863         if (strength < 2) {
23864             //this.markInvalid(this.errors.TooWeak);
23865             this.errorMsg = this.errors.TooWeak;
23866             //return false;
23867         }
23868         
23869         
23870         console.log('strength2: ' + strength);
23871         
23872         //var pm = this.trigger.child('div/div/div').dom;
23873         
23874         var pm = this.trigger.child('div/div');
23875         pm.removeClass(this.meterClass);
23876         pm.addClass(this.meterClass[strength]);
23877                 
23878         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23879                 
23880         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23881         
23882         this.errorMsg = ''; 
23883         return true;
23884     },
23885     // private
23886     CharacterSetChecks: function (type)
23887     {
23888         this.type = type;
23889         this.fResult = false;
23890     },
23891     // private
23892     isctype: function (character, type)
23893     {
23894         switch (type) {  
23895             case this.kCapitalLetter:
23896                 if (character >= 'A' && character <= 'Z') {
23897                     return true;
23898                 }
23899                 break;
23900             
23901             case this.kSmallLetter:
23902                 if (character >= 'a' && character <= 'z') {
23903                     return true;
23904                 }
23905                 break;
23906             
23907             case this.kDigit:
23908                 if (character >= '0' && character <= '9') {
23909                     return true;
23910                 }
23911                 break;
23912             
23913             case this.kPunctuation:
23914                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23915                     return true;
23916                 }
23917                 break;
23918             
23919             default:
23920                 return false;
23921         }
23922
23923     },
23924     // private
23925     IsLongEnough: function (pwd, size)
23926     {
23927         return !(pwd == null || isNaN(size) || pwd.length < size);
23928     },
23929     // private
23930     SpansEnoughCharacterSets: function (word, nb)
23931     {
23932         if (!this.IsLongEnough(word, nb))
23933         {
23934             return false;
23935         }
23936
23937         var characterSetChecks = new Array(
23938             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23939             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23940         );
23941         
23942         for (var index = 0; index < word.length; ++index) {
23943             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23944                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23945                     characterSetChecks[nCharSet].fResult = true;
23946                     break;
23947                 }
23948             }
23949         }
23950
23951         var nCharSets = 0;
23952         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23953             if (characterSetChecks[nCharSet].fResult) {
23954                 ++nCharSets;
23955             }
23956         }
23957
23958         if (nCharSets < nb) {
23959             return false;
23960         }
23961         return true;
23962     },
23963     // private
23964     ClientSideStrongPassword: function (pwd)
23965     {
23966         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23967     },
23968     // private
23969     ClientSideMediumPassword: function (pwd)
23970     {
23971         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23972     },
23973     // private
23974     ClientSideWeakPassword: function (pwd)
23975     {
23976         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23977     }
23978           
23979 })//<script type="text/javascript">
23980
23981 /*
23982  * Based  Ext JS Library 1.1.1
23983  * Copyright(c) 2006-2007, Ext JS, LLC.
23984  * LGPL
23985  *
23986  */
23987  
23988 /**
23989  * @class Roo.HtmlEditorCore
23990  * @extends Roo.Component
23991  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23992  *
23993  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23994  */
23995
23996 Roo.HtmlEditorCore = function(config){
23997     
23998     
23999     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24000     
24001     
24002     this.addEvents({
24003         /**
24004          * @event initialize
24005          * Fires when the editor is fully initialized (including the iframe)
24006          * @param {Roo.HtmlEditorCore} this
24007          */
24008         initialize: true,
24009         /**
24010          * @event activate
24011          * Fires when the editor is first receives the focus. Any insertion must wait
24012          * until after this event.
24013          * @param {Roo.HtmlEditorCore} this
24014          */
24015         activate: true,
24016          /**
24017          * @event beforesync
24018          * Fires before the textarea is updated with content from the editor iframe. Return false
24019          * to cancel the sync.
24020          * @param {Roo.HtmlEditorCore} this
24021          * @param {String} html
24022          */
24023         beforesync: true,
24024          /**
24025          * @event beforepush
24026          * Fires before the iframe editor is updated with content from the textarea. Return false
24027          * to cancel the push.
24028          * @param {Roo.HtmlEditorCore} this
24029          * @param {String} html
24030          */
24031         beforepush: true,
24032          /**
24033          * @event sync
24034          * Fires when the textarea is updated with content from the editor iframe.
24035          * @param {Roo.HtmlEditorCore} this
24036          * @param {String} html
24037          */
24038         sync: true,
24039          /**
24040          * @event push
24041          * Fires when the iframe editor is updated with content from the textarea.
24042          * @param {Roo.HtmlEditorCore} this
24043          * @param {String} html
24044          */
24045         push: true,
24046         
24047         /**
24048          * @event editorevent
24049          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24050          * @param {Roo.HtmlEditorCore} this
24051          */
24052         editorevent: true
24053         
24054     });
24055     
24056     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24057     
24058     // defaults : white / black...
24059     this.applyBlacklists();
24060     
24061     
24062     
24063 };
24064
24065
24066 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24067
24068
24069      /**
24070      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24071      */
24072     
24073     owner : false,
24074     
24075      /**
24076      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24077      *                        Roo.resizable.
24078      */
24079     resizable : false,
24080      /**
24081      * @cfg {Number} height (in pixels)
24082      */   
24083     height: 300,
24084    /**
24085      * @cfg {Number} width (in pixels)
24086      */   
24087     width: 500,
24088     
24089     /**
24090      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24091      * 
24092      */
24093     stylesheets: false,
24094     
24095     // id of frame..
24096     frameId: false,
24097     
24098     // private properties
24099     validationEvent : false,
24100     deferHeight: true,
24101     initialized : false,
24102     activated : false,
24103     sourceEditMode : false,
24104     onFocus : Roo.emptyFn,
24105     iframePad:3,
24106     hideMode:'offsets',
24107     
24108     clearUp: true,
24109     
24110     // blacklist + whitelisted elements..
24111     black: false,
24112     white: false,
24113      
24114     bodyCls : '',
24115
24116     /**
24117      * Protected method that will not generally be called directly. It
24118      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24119      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24120      */
24121     getDocMarkup : function(){
24122         // body styles..
24123         var st = '';
24124         
24125         // inherit styels from page...?? 
24126         if (this.stylesheets === false) {
24127             
24128             Roo.get(document.head).select('style').each(function(node) {
24129                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24130             });
24131             
24132             Roo.get(document.head).select('link').each(function(node) { 
24133                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24134             });
24135             
24136         } else if (!this.stylesheets.length) {
24137                 // simple..
24138                 st = '<style type="text/css">' +
24139                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24140                    '</style>';
24141         } else {
24142             for (var i in this.stylesheets) { 
24143                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24144             }
24145             
24146         }
24147         
24148         st +=  '<style type="text/css">' +
24149             'IMG { cursor: pointer } ' +
24150         '</style>';
24151
24152         var cls = 'roo-htmleditor-body';
24153         
24154         if(this.bodyCls.length){
24155             cls += ' ' + this.bodyCls;
24156         }
24157         
24158         return '<html><head>' + st  +
24159             //<style type="text/css">' +
24160             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24161             //'</style>' +
24162             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24163     },
24164
24165     // private
24166     onRender : function(ct, position)
24167     {
24168         var _t = this;
24169         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24170         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24171         
24172         
24173         this.el.dom.style.border = '0 none';
24174         this.el.dom.setAttribute('tabIndex', -1);
24175         this.el.addClass('x-hidden hide');
24176         
24177         
24178         
24179         if(Roo.isIE){ // fix IE 1px bogus margin
24180             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24181         }
24182        
24183         
24184         this.frameId = Roo.id();
24185         
24186          
24187         
24188         var iframe = this.owner.wrap.createChild({
24189             tag: 'iframe',
24190             cls: 'form-control', // bootstrap..
24191             id: this.frameId,
24192             name: this.frameId,
24193             frameBorder : 'no',
24194             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24195         }, this.el
24196         );
24197         
24198         
24199         this.iframe = iframe.dom;
24200
24201          this.assignDocWin();
24202         
24203         this.doc.designMode = 'on';
24204        
24205         this.doc.open();
24206         this.doc.write(this.getDocMarkup());
24207         this.doc.close();
24208
24209         
24210         var task = { // must defer to wait for browser to be ready
24211             run : function(){
24212                 //console.log("run task?" + this.doc.readyState);
24213                 this.assignDocWin();
24214                 if(this.doc.body || this.doc.readyState == 'complete'){
24215                     try {
24216                         this.doc.designMode="on";
24217                     } catch (e) {
24218                         return;
24219                     }
24220                     Roo.TaskMgr.stop(task);
24221                     this.initEditor.defer(10, this);
24222                 }
24223             },
24224             interval : 10,
24225             duration: 10000,
24226             scope: this
24227         };
24228         Roo.TaskMgr.start(task);
24229
24230     },
24231
24232     // private
24233     onResize : function(w, h)
24234     {
24235          Roo.log('resize: ' +w + ',' + h );
24236         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24237         if(!this.iframe){
24238             return;
24239         }
24240         if(typeof w == 'number'){
24241             
24242             this.iframe.style.width = w + 'px';
24243         }
24244         if(typeof h == 'number'){
24245             
24246             this.iframe.style.height = h + 'px';
24247             if(this.doc){
24248                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24249             }
24250         }
24251         
24252     },
24253
24254     /**
24255      * Toggles the editor between standard and source edit mode.
24256      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24257      */
24258     toggleSourceEdit : function(sourceEditMode){
24259         
24260         this.sourceEditMode = sourceEditMode === true;
24261         
24262         if(this.sourceEditMode){
24263  
24264             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24265             
24266         }else{
24267             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24268             //this.iframe.className = '';
24269             this.deferFocus();
24270         }
24271         //this.setSize(this.owner.wrap.getSize());
24272         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24273     },
24274
24275     
24276   
24277
24278     /**
24279      * Protected method that will not generally be called directly. If you need/want
24280      * custom HTML cleanup, this is the method you should override.
24281      * @param {String} html The HTML to be cleaned
24282      * return {String} The cleaned HTML
24283      */
24284     cleanHtml : function(html){
24285         html = String(html);
24286         if(html.length > 5){
24287             if(Roo.isSafari){ // strip safari nonsense
24288                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24289             }
24290         }
24291         if(html == '&nbsp;'){
24292             html = '';
24293         }
24294         return html;
24295     },
24296
24297     /**
24298      * HTML Editor -> Textarea
24299      * Protected method that will not generally be called directly. Syncs the contents
24300      * of the editor iframe with the textarea.
24301      */
24302     syncValue : function(){
24303         if(this.initialized){
24304             var bd = (this.doc.body || this.doc.documentElement);
24305             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24306             var html = bd.innerHTML;
24307             if(Roo.isSafari){
24308                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24309                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24310                 if(m && m[1]){
24311                     html = '<div style="'+m[0]+'">' + html + '</div>';
24312                 }
24313             }
24314             html = this.cleanHtml(html);
24315             // fix up the special chars.. normaly like back quotes in word...
24316             // however we do not want to do this with chinese..
24317             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24318                 
24319                 var cc = match.charCodeAt();
24320
24321                 // Get the character value, handling surrogate pairs
24322                 if (match.length == 2) {
24323                     // It's a surrogate pair, calculate the Unicode code point
24324                     var high = match.charCodeAt(0) - 0xD800;
24325                     var low  = match.charCodeAt(1) - 0xDC00;
24326                     cc = (high * 0x400) + low + 0x10000;
24327                 }  else if (
24328                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24329                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24330                     (cc >= 0xf900 && cc < 0xfb00 )
24331                 ) {
24332                         return match;
24333                 }  
24334          
24335                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24336                 return "&#" + cc + ";";
24337                 
24338                 
24339             });
24340             
24341             
24342              
24343             if(this.owner.fireEvent('beforesync', this, html) !== false){
24344                 this.el.dom.value = html;
24345                 this.owner.fireEvent('sync', this, html);
24346             }
24347         }
24348     },
24349
24350     /**
24351      * Protected method that will not generally be called directly. Pushes the value of the textarea
24352      * into the iframe editor.
24353      */
24354     pushValue : function(){
24355         if(this.initialized){
24356             var v = this.el.dom.value.trim();
24357             
24358 //            if(v.length < 1){
24359 //                v = '&#160;';
24360 //            }
24361             
24362             if(this.owner.fireEvent('beforepush', this, v) !== false){
24363                 var d = (this.doc.body || this.doc.documentElement);
24364                 d.innerHTML = v;
24365                 this.cleanUpPaste();
24366                 this.el.dom.value = d.innerHTML;
24367                 this.owner.fireEvent('push', this, v);
24368             }
24369         }
24370     },
24371
24372     // private
24373     deferFocus : function(){
24374         this.focus.defer(10, this);
24375     },
24376
24377     // doc'ed in Field
24378     focus : function(){
24379         if(this.win && !this.sourceEditMode){
24380             this.win.focus();
24381         }else{
24382             this.el.focus();
24383         }
24384     },
24385     
24386     assignDocWin: function()
24387     {
24388         var iframe = this.iframe;
24389         
24390          if(Roo.isIE){
24391             this.doc = iframe.contentWindow.document;
24392             this.win = iframe.contentWindow;
24393         } else {
24394 //            if (!Roo.get(this.frameId)) {
24395 //                return;
24396 //            }
24397 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24398 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24399             
24400             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24401                 return;
24402             }
24403             
24404             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24405             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24406         }
24407     },
24408     
24409     // private
24410     initEditor : function(){
24411         //console.log("INIT EDITOR");
24412         this.assignDocWin();
24413         
24414         
24415         
24416         this.doc.designMode="on";
24417         this.doc.open();
24418         this.doc.write(this.getDocMarkup());
24419         this.doc.close();
24420         
24421         var dbody = (this.doc.body || this.doc.documentElement);
24422         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24423         // this copies styles from the containing element into thsi one..
24424         // not sure why we need all of this..
24425         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24426         
24427         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24428         //ss['background-attachment'] = 'fixed'; // w3c
24429         dbody.bgProperties = 'fixed'; // ie
24430         //Roo.DomHelper.applyStyles(dbody, ss);
24431         Roo.EventManager.on(this.doc, {
24432             //'mousedown': this.onEditorEvent,
24433             'mouseup': this.onEditorEvent,
24434             'dblclick': this.onEditorEvent,
24435             'click': this.onEditorEvent,
24436             'keyup': this.onEditorEvent,
24437             buffer:100,
24438             scope: this
24439         });
24440         if(Roo.isGecko){
24441             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24442         }
24443         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24444             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24445         }
24446         this.initialized = true;
24447
24448         this.owner.fireEvent('initialize', this);
24449         this.pushValue();
24450     },
24451
24452     // private
24453     onDestroy : function(){
24454         
24455         
24456         
24457         if(this.rendered){
24458             
24459             //for (var i =0; i < this.toolbars.length;i++) {
24460             //    // fixme - ask toolbars for heights?
24461             //    this.toolbars[i].onDestroy();
24462            // }
24463             
24464             //this.wrap.dom.innerHTML = '';
24465             //this.wrap.remove();
24466         }
24467     },
24468
24469     // private
24470     onFirstFocus : function(){
24471         
24472         this.assignDocWin();
24473         
24474         
24475         this.activated = true;
24476          
24477     
24478         if(Roo.isGecko){ // prevent silly gecko errors
24479             this.win.focus();
24480             var s = this.win.getSelection();
24481             if(!s.focusNode || s.focusNode.nodeType != 3){
24482                 var r = s.getRangeAt(0);
24483                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24484                 r.collapse(true);
24485                 this.deferFocus();
24486             }
24487             try{
24488                 this.execCmd('useCSS', true);
24489                 this.execCmd('styleWithCSS', false);
24490             }catch(e){}
24491         }
24492         this.owner.fireEvent('activate', this);
24493     },
24494
24495     // private
24496     adjustFont: function(btn){
24497         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24498         //if(Roo.isSafari){ // safari
24499         //    adjust *= 2;
24500        // }
24501         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24502         if(Roo.isSafari){ // safari
24503             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24504             v =  (v < 10) ? 10 : v;
24505             v =  (v > 48) ? 48 : v;
24506             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24507             
24508         }
24509         
24510         
24511         v = Math.max(1, v+adjust);
24512         
24513         this.execCmd('FontSize', v  );
24514     },
24515
24516     onEditorEvent : function(e)
24517     {
24518         this.owner.fireEvent('editorevent', this, e);
24519       //  this.updateToolbar();
24520         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24521     },
24522
24523     insertTag : function(tg)
24524     {
24525         // could be a bit smarter... -> wrap the current selected tRoo..
24526         if (tg.toLowerCase() == 'span' ||
24527             tg.toLowerCase() == 'code' ||
24528             tg.toLowerCase() == 'sup' ||
24529             tg.toLowerCase() == 'sub' 
24530             ) {
24531             
24532             range = this.createRange(this.getSelection());
24533             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24534             wrappingNode.appendChild(range.extractContents());
24535             range.insertNode(wrappingNode);
24536
24537             return;
24538             
24539             
24540             
24541         }
24542         this.execCmd("formatblock",   tg);
24543         
24544     },
24545     
24546     insertText : function(txt)
24547     {
24548         
24549         
24550         var range = this.createRange();
24551         range.deleteContents();
24552                //alert(Sender.getAttribute('label'));
24553                
24554         range.insertNode(this.doc.createTextNode(txt));
24555     } ,
24556     
24557      
24558
24559     /**
24560      * Executes a Midas editor command on the editor document and performs necessary focus and
24561      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24562      * @param {String} cmd The Midas command
24563      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24564      */
24565     relayCmd : function(cmd, value){
24566         this.win.focus();
24567         this.execCmd(cmd, value);
24568         this.owner.fireEvent('editorevent', this);
24569         //this.updateToolbar();
24570         this.owner.deferFocus();
24571     },
24572
24573     /**
24574      * Executes a Midas editor command directly on the editor document.
24575      * For visual commands, you should use {@link #relayCmd} instead.
24576      * <b>This should only be called after the editor is initialized.</b>
24577      * @param {String} cmd The Midas command
24578      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24579      */
24580     execCmd : function(cmd, value){
24581         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24582         this.syncValue();
24583     },
24584  
24585  
24586    
24587     /**
24588      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24589      * to insert tRoo.
24590      * @param {String} text | dom node.. 
24591      */
24592     insertAtCursor : function(text)
24593     {
24594         
24595         if(!this.activated){
24596             return;
24597         }
24598         /*
24599         if(Roo.isIE){
24600             this.win.focus();
24601             var r = this.doc.selection.createRange();
24602             if(r){
24603                 r.collapse(true);
24604                 r.pasteHTML(text);
24605                 this.syncValue();
24606                 this.deferFocus();
24607             
24608             }
24609             return;
24610         }
24611         */
24612         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24613             this.win.focus();
24614             
24615             
24616             // from jquery ui (MIT licenced)
24617             var range, node;
24618             var win = this.win;
24619             
24620             if (win.getSelection && win.getSelection().getRangeAt) {
24621                 range = win.getSelection().getRangeAt(0);
24622                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24623                 range.insertNode(node);
24624             } else if (win.document.selection && win.document.selection.createRange) {
24625                 // no firefox support
24626                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24627                 win.document.selection.createRange().pasteHTML(txt);
24628             } else {
24629                 // no firefox support
24630                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24631                 this.execCmd('InsertHTML', txt);
24632             } 
24633             
24634             this.syncValue();
24635             
24636             this.deferFocus();
24637         }
24638     },
24639  // private
24640     mozKeyPress : function(e){
24641         if(e.ctrlKey){
24642             var c = e.getCharCode(), cmd;
24643           
24644             if(c > 0){
24645                 c = String.fromCharCode(c).toLowerCase();
24646                 switch(c){
24647                     case 'b':
24648                         cmd = 'bold';
24649                         break;
24650                     case 'i':
24651                         cmd = 'italic';
24652                         break;
24653                     
24654                     case 'u':
24655                         cmd = 'underline';
24656                         break;
24657                     
24658                     case 'v':
24659                         this.cleanUpPaste.defer(100, this);
24660                         return;
24661                         
24662                 }
24663                 if(cmd){
24664                     this.win.focus();
24665                     this.execCmd(cmd);
24666                     this.deferFocus();
24667                     e.preventDefault();
24668                 }
24669                 
24670             }
24671         }
24672     },
24673
24674     // private
24675     fixKeys : function(){ // load time branching for fastest keydown performance
24676         if(Roo.isIE){
24677             return function(e){
24678                 var k = e.getKey(), r;
24679                 if(k == e.TAB){
24680                     e.stopEvent();
24681                     r = this.doc.selection.createRange();
24682                     if(r){
24683                         r.collapse(true);
24684                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24685                         this.deferFocus();
24686                     }
24687                     return;
24688                 }
24689                 
24690                 if(k == e.ENTER){
24691                     r = this.doc.selection.createRange();
24692                     if(r){
24693                         var target = r.parentElement();
24694                         if(!target || target.tagName.toLowerCase() != 'li'){
24695                             e.stopEvent();
24696                             r.pasteHTML('<br />');
24697                             r.collapse(false);
24698                             r.select();
24699                         }
24700                     }
24701                 }
24702                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24703                     this.cleanUpPaste.defer(100, this);
24704                     return;
24705                 }
24706                 
24707                 
24708             };
24709         }else if(Roo.isOpera){
24710             return function(e){
24711                 var k = e.getKey();
24712                 if(k == e.TAB){
24713                     e.stopEvent();
24714                     this.win.focus();
24715                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24716                     this.deferFocus();
24717                 }
24718                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24719                     this.cleanUpPaste.defer(100, this);
24720                     return;
24721                 }
24722                 
24723             };
24724         }else if(Roo.isSafari){
24725             return function(e){
24726                 var k = e.getKey();
24727                 
24728                 if(k == e.TAB){
24729                     e.stopEvent();
24730                     this.execCmd('InsertText','\t');
24731                     this.deferFocus();
24732                     return;
24733                 }
24734                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24735                     this.cleanUpPaste.defer(100, this);
24736                     return;
24737                 }
24738                 
24739              };
24740         }
24741     }(),
24742     
24743     getAllAncestors: function()
24744     {
24745         var p = this.getSelectedNode();
24746         var a = [];
24747         if (!p) {
24748             a.push(p); // push blank onto stack..
24749             p = this.getParentElement();
24750         }
24751         
24752         
24753         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24754             a.push(p);
24755             p = p.parentNode;
24756         }
24757         a.push(this.doc.body);
24758         return a;
24759     },
24760     lastSel : false,
24761     lastSelNode : false,
24762     
24763     
24764     getSelection : function() 
24765     {
24766         this.assignDocWin();
24767         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24768     },
24769     
24770     getSelectedNode: function() 
24771     {
24772         // this may only work on Gecko!!!
24773         
24774         // should we cache this!!!!
24775         
24776         
24777         
24778          
24779         var range = this.createRange(this.getSelection()).cloneRange();
24780         
24781         if (Roo.isIE) {
24782             var parent = range.parentElement();
24783             while (true) {
24784                 var testRange = range.duplicate();
24785                 testRange.moveToElementText(parent);
24786                 if (testRange.inRange(range)) {
24787                     break;
24788                 }
24789                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24790                     break;
24791                 }
24792                 parent = parent.parentElement;
24793             }
24794             return parent;
24795         }
24796         
24797         // is ancestor a text element.
24798         var ac =  range.commonAncestorContainer;
24799         if (ac.nodeType == 3) {
24800             ac = ac.parentNode;
24801         }
24802         
24803         var ar = ac.childNodes;
24804          
24805         var nodes = [];
24806         var other_nodes = [];
24807         var has_other_nodes = false;
24808         for (var i=0;i<ar.length;i++) {
24809             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24810                 continue;
24811             }
24812             // fullly contained node.
24813             
24814             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24815                 nodes.push(ar[i]);
24816                 continue;
24817             }
24818             
24819             // probably selected..
24820             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24821                 other_nodes.push(ar[i]);
24822                 continue;
24823             }
24824             // outer..
24825             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24826                 continue;
24827             }
24828             
24829             
24830             has_other_nodes = true;
24831         }
24832         if (!nodes.length && other_nodes.length) {
24833             nodes= other_nodes;
24834         }
24835         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24836             return false;
24837         }
24838         
24839         return nodes[0];
24840     },
24841     createRange: function(sel)
24842     {
24843         // this has strange effects when using with 
24844         // top toolbar - not sure if it's a great idea.
24845         //this.editor.contentWindow.focus();
24846         if (typeof sel != "undefined") {
24847             try {
24848                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24849             } catch(e) {
24850                 return this.doc.createRange();
24851             }
24852         } else {
24853             return this.doc.createRange();
24854         }
24855     },
24856     getParentElement: function()
24857     {
24858         
24859         this.assignDocWin();
24860         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24861         
24862         var range = this.createRange(sel);
24863          
24864         try {
24865             var p = range.commonAncestorContainer;
24866             while (p.nodeType == 3) { // text node
24867                 p = p.parentNode;
24868             }
24869             return p;
24870         } catch (e) {
24871             return null;
24872         }
24873     
24874     },
24875     /***
24876      *
24877      * Range intersection.. the hard stuff...
24878      *  '-1' = before
24879      *  '0' = hits..
24880      *  '1' = after.
24881      *         [ -- selected range --- ]
24882      *   [fail]                        [fail]
24883      *
24884      *    basically..
24885      *      if end is before start or  hits it. fail.
24886      *      if start is after end or hits it fail.
24887      *
24888      *   if either hits (but other is outside. - then it's not 
24889      *   
24890      *    
24891      **/
24892     
24893     
24894     // @see http://www.thismuchiknow.co.uk/?p=64.
24895     rangeIntersectsNode : function(range, node)
24896     {
24897         var nodeRange = node.ownerDocument.createRange();
24898         try {
24899             nodeRange.selectNode(node);
24900         } catch (e) {
24901             nodeRange.selectNodeContents(node);
24902         }
24903     
24904         var rangeStartRange = range.cloneRange();
24905         rangeStartRange.collapse(true);
24906     
24907         var rangeEndRange = range.cloneRange();
24908         rangeEndRange.collapse(false);
24909     
24910         var nodeStartRange = nodeRange.cloneRange();
24911         nodeStartRange.collapse(true);
24912     
24913         var nodeEndRange = nodeRange.cloneRange();
24914         nodeEndRange.collapse(false);
24915     
24916         return rangeStartRange.compareBoundaryPoints(
24917                  Range.START_TO_START, nodeEndRange) == -1 &&
24918                rangeEndRange.compareBoundaryPoints(
24919                  Range.START_TO_START, nodeStartRange) == 1;
24920         
24921          
24922     },
24923     rangeCompareNode : function(range, node)
24924     {
24925         var nodeRange = node.ownerDocument.createRange();
24926         try {
24927             nodeRange.selectNode(node);
24928         } catch (e) {
24929             nodeRange.selectNodeContents(node);
24930         }
24931         
24932         
24933         range.collapse(true);
24934     
24935         nodeRange.collapse(true);
24936      
24937         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24938         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24939          
24940         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24941         
24942         var nodeIsBefore   =  ss == 1;
24943         var nodeIsAfter    = ee == -1;
24944         
24945         if (nodeIsBefore && nodeIsAfter) {
24946             return 0; // outer
24947         }
24948         if (!nodeIsBefore && nodeIsAfter) {
24949             return 1; //right trailed.
24950         }
24951         
24952         if (nodeIsBefore && !nodeIsAfter) {
24953             return 2;  // left trailed.
24954         }
24955         // fully contined.
24956         return 3;
24957     },
24958
24959     // private? - in a new class?
24960     cleanUpPaste :  function()
24961     {
24962         // cleans up the whole document..
24963         Roo.log('cleanuppaste');
24964         
24965         this.cleanUpChildren(this.doc.body);
24966         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24967         if (clean != this.doc.body.innerHTML) {
24968             this.doc.body.innerHTML = clean;
24969         }
24970         
24971     },
24972     
24973     cleanWordChars : function(input) {// change the chars to hex code
24974         var he = Roo.HtmlEditorCore;
24975         
24976         var output = input;
24977         Roo.each(he.swapCodes, function(sw) { 
24978             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24979             
24980             output = output.replace(swapper, sw[1]);
24981         });
24982         
24983         return output;
24984     },
24985     
24986     
24987     cleanUpChildren : function (n)
24988     {
24989         if (!n.childNodes.length) {
24990             return;
24991         }
24992         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24993            this.cleanUpChild(n.childNodes[i]);
24994         }
24995     },
24996     
24997     
24998         
24999     
25000     cleanUpChild : function (node)
25001     {
25002         var ed = this;
25003         //console.log(node);
25004         if (node.nodeName == "#text") {
25005             // clean up silly Windows -- stuff?
25006             return; 
25007         }
25008         if (node.nodeName == "#comment") {
25009             node.parentNode.removeChild(node);
25010             // clean up silly Windows -- stuff?
25011             return; 
25012         }
25013         var lcname = node.tagName.toLowerCase();
25014         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25015         // whitelist of tags..
25016         
25017         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25018             // remove node.
25019             node.parentNode.removeChild(node);
25020             return;
25021             
25022         }
25023         
25024         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25025         
25026         // spans with no attributes - just remove them..
25027         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25028             remove_keep_children = true;
25029         }
25030         
25031         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25032         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25033         
25034         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25035         //    remove_keep_children = true;
25036         //}
25037         
25038         if (remove_keep_children) {
25039             this.cleanUpChildren(node);
25040             // inserts everything just before this node...
25041             while (node.childNodes.length) {
25042                 var cn = node.childNodes[0];
25043                 node.removeChild(cn);
25044                 node.parentNode.insertBefore(cn, node);
25045             }
25046             node.parentNode.removeChild(node);
25047             return;
25048         }
25049         
25050         if (!node.attributes || !node.attributes.length) {
25051             
25052           
25053             
25054             
25055             this.cleanUpChildren(node);
25056             return;
25057         }
25058         
25059         function cleanAttr(n,v)
25060         {
25061             
25062             if (v.match(/^\./) || v.match(/^\//)) {
25063                 return;
25064             }
25065             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25066                 return;
25067             }
25068             if (v.match(/^#/)) {
25069                 return;
25070             }
25071             if (v.match(/^\{/)) { // allow template editing.
25072                 return;
25073             }
25074 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25075             node.removeAttribute(n);
25076             
25077         }
25078         
25079         var cwhite = this.cwhite;
25080         var cblack = this.cblack;
25081             
25082         function cleanStyle(n,v)
25083         {
25084             if (v.match(/expression/)) { //XSS?? should we even bother..
25085                 node.removeAttribute(n);
25086                 return;
25087             }
25088             
25089             var parts = v.split(/;/);
25090             var clean = [];
25091             
25092             Roo.each(parts, function(p) {
25093                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25094                 if (!p.length) {
25095                     return true;
25096                 }
25097                 var l = p.split(':').shift().replace(/\s+/g,'');
25098                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25099                 
25100                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25101 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25102                     //node.removeAttribute(n);
25103                     return true;
25104                 }
25105                 //Roo.log()
25106                 // only allow 'c whitelisted system attributes'
25107                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25108 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25109                     //node.removeAttribute(n);
25110                     return true;
25111                 }
25112                 
25113                 
25114                  
25115                 
25116                 clean.push(p);
25117                 return true;
25118             });
25119             if (clean.length) { 
25120                 node.setAttribute(n, clean.join(';'));
25121             } else {
25122                 node.removeAttribute(n);
25123             }
25124             
25125         }
25126         
25127         
25128         for (var i = node.attributes.length-1; i > -1 ; i--) {
25129             var a = node.attributes[i];
25130             //console.log(a);
25131             
25132             if (a.name.toLowerCase().substr(0,2)=='on')  {
25133                 node.removeAttribute(a.name);
25134                 continue;
25135             }
25136             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25137                 node.removeAttribute(a.name);
25138                 continue;
25139             }
25140             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25141                 cleanAttr(a.name,a.value); // fixme..
25142                 continue;
25143             }
25144             if (a.name == 'style') {
25145                 cleanStyle(a.name,a.value);
25146                 continue;
25147             }
25148             /// clean up MS crap..
25149             // tecnically this should be a list of valid class'es..
25150             
25151             
25152             if (a.name == 'class') {
25153                 if (a.value.match(/^Mso/)) {
25154                     node.removeAttribute('class');
25155                 }
25156                 
25157                 if (a.value.match(/^body$/)) {
25158                     node.removeAttribute('class');
25159                 }
25160                 continue;
25161             }
25162             
25163             // style cleanup!?
25164             // class cleanup?
25165             
25166         }
25167         
25168         
25169         this.cleanUpChildren(node);
25170         
25171         
25172     },
25173     
25174     /**
25175      * Clean up MS wordisms...
25176      */
25177     cleanWord : function(node)
25178     {
25179         if (!node) {
25180             this.cleanWord(this.doc.body);
25181             return;
25182         }
25183         
25184         if(
25185                 node.nodeName == 'SPAN' &&
25186                 !node.hasAttributes() &&
25187                 node.childNodes.length == 1 &&
25188                 node.firstChild.nodeName == "#text"  
25189         ) {
25190             var textNode = node.firstChild;
25191             node.removeChild(textNode);
25192             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25193                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25194             }
25195             node.parentNode.insertBefore(textNode, node);
25196             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25197                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25198             }
25199             node.parentNode.removeChild(node);
25200         }
25201         
25202         if (node.nodeName == "#text") {
25203             // clean up silly Windows -- stuff?
25204             return; 
25205         }
25206         if (node.nodeName == "#comment") {
25207             node.parentNode.removeChild(node);
25208             // clean up silly Windows -- stuff?
25209             return; 
25210         }
25211         
25212         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25213             node.parentNode.removeChild(node);
25214             return;
25215         }
25216         //Roo.log(node.tagName);
25217         // remove - but keep children..
25218         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25219             //Roo.log('-- removed');
25220             while (node.childNodes.length) {
25221                 var cn = node.childNodes[0];
25222                 node.removeChild(cn);
25223                 node.parentNode.insertBefore(cn, node);
25224                 // move node to parent - and clean it..
25225                 this.cleanWord(cn);
25226             }
25227             node.parentNode.removeChild(node);
25228             /// no need to iterate chidlren = it's got none..
25229             //this.iterateChildren(node, this.cleanWord);
25230             return;
25231         }
25232         // clean styles
25233         if (node.className.length) {
25234             
25235             var cn = node.className.split(/\W+/);
25236             var cna = [];
25237             Roo.each(cn, function(cls) {
25238                 if (cls.match(/Mso[a-zA-Z]+/)) {
25239                     return;
25240                 }
25241                 cna.push(cls);
25242             });
25243             node.className = cna.length ? cna.join(' ') : '';
25244             if (!cna.length) {
25245                 node.removeAttribute("class");
25246             }
25247         }
25248         
25249         if (node.hasAttribute("lang")) {
25250             node.removeAttribute("lang");
25251         }
25252         
25253         if (node.hasAttribute("style")) {
25254             
25255             var styles = node.getAttribute("style").split(";");
25256             var nstyle = [];
25257             Roo.each(styles, function(s) {
25258                 if (!s.match(/:/)) {
25259                     return;
25260                 }
25261                 var kv = s.split(":");
25262                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25263                     return;
25264                 }
25265                 // what ever is left... we allow.
25266                 nstyle.push(s);
25267             });
25268             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25269             if (!nstyle.length) {
25270                 node.removeAttribute('style');
25271             }
25272         }
25273         this.iterateChildren(node, this.cleanWord);
25274         
25275         
25276         
25277     },
25278     /**
25279      * iterateChildren of a Node, calling fn each time, using this as the scole..
25280      * @param {DomNode} node node to iterate children of.
25281      * @param {Function} fn method of this class to call on each item.
25282      */
25283     iterateChildren : function(node, fn)
25284     {
25285         if (!node.childNodes.length) {
25286                 return;
25287         }
25288         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25289            fn.call(this, node.childNodes[i])
25290         }
25291     },
25292     
25293     
25294     /**
25295      * cleanTableWidths.
25296      *
25297      * Quite often pasting from word etc.. results in tables with column and widths.
25298      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25299      *
25300      */
25301     cleanTableWidths : function(node)
25302     {
25303          
25304          
25305         if (!node) {
25306             this.cleanTableWidths(this.doc.body);
25307             return;
25308         }
25309         
25310         // ignore list...
25311         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25312             return; 
25313         }
25314         Roo.log(node.tagName);
25315         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25316             this.iterateChildren(node, this.cleanTableWidths);
25317             return;
25318         }
25319         if (node.hasAttribute('width')) {
25320             node.removeAttribute('width');
25321         }
25322         
25323          
25324         if (node.hasAttribute("style")) {
25325             // pretty basic...
25326             
25327             var styles = node.getAttribute("style").split(";");
25328             var nstyle = [];
25329             Roo.each(styles, function(s) {
25330                 if (!s.match(/:/)) {
25331                     return;
25332                 }
25333                 var kv = s.split(":");
25334                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25335                     return;
25336                 }
25337                 // what ever is left... we allow.
25338                 nstyle.push(s);
25339             });
25340             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25341             if (!nstyle.length) {
25342                 node.removeAttribute('style');
25343             }
25344         }
25345         
25346         this.iterateChildren(node, this.cleanTableWidths);
25347         
25348         
25349     },
25350     
25351     
25352     
25353     
25354     domToHTML : function(currentElement, depth, nopadtext) {
25355         
25356         depth = depth || 0;
25357         nopadtext = nopadtext || false;
25358     
25359         if (!currentElement) {
25360             return this.domToHTML(this.doc.body);
25361         }
25362         
25363         //Roo.log(currentElement);
25364         var j;
25365         var allText = false;
25366         var nodeName = currentElement.nodeName;
25367         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25368         
25369         if  (nodeName == '#text') {
25370             
25371             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25372         }
25373         
25374         
25375         var ret = '';
25376         if (nodeName != 'BODY') {
25377              
25378             var i = 0;
25379             // Prints the node tagName, such as <A>, <IMG>, etc
25380             if (tagName) {
25381                 var attr = [];
25382                 for(i = 0; i < currentElement.attributes.length;i++) {
25383                     // quoting?
25384                     var aname = currentElement.attributes.item(i).name;
25385                     if (!currentElement.attributes.item(i).value.length) {
25386                         continue;
25387                     }
25388                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25389                 }
25390                 
25391                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25392             } 
25393             else {
25394                 
25395                 // eack
25396             }
25397         } else {
25398             tagName = false;
25399         }
25400         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25401             return ret;
25402         }
25403         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25404             nopadtext = true;
25405         }
25406         
25407         
25408         // Traverse the tree
25409         i = 0;
25410         var currentElementChild = currentElement.childNodes.item(i);
25411         var allText = true;
25412         var innerHTML  = '';
25413         lastnode = '';
25414         while (currentElementChild) {
25415             // Formatting code (indent the tree so it looks nice on the screen)
25416             var nopad = nopadtext;
25417             if (lastnode == 'SPAN') {
25418                 nopad  = true;
25419             }
25420             // text
25421             if  (currentElementChild.nodeName == '#text') {
25422                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25423                 toadd = nopadtext ? toadd : toadd.trim();
25424                 if (!nopad && toadd.length > 80) {
25425                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25426                 }
25427                 innerHTML  += toadd;
25428                 
25429                 i++;
25430                 currentElementChild = currentElement.childNodes.item(i);
25431                 lastNode = '';
25432                 continue;
25433             }
25434             allText = false;
25435             
25436             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25437                 
25438             // Recursively traverse the tree structure of the child node
25439             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25440             lastnode = currentElementChild.nodeName;
25441             i++;
25442             currentElementChild=currentElement.childNodes.item(i);
25443         }
25444         
25445         ret += innerHTML;
25446         
25447         if (!allText) {
25448                 // The remaining code is mostly for formatting the tree
25449             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25450         }
25451         
25452         
25453         if (tagName) {
25454             ret+= "</"+tagName+">";
25455         }
25456         return ret;
25457         
25458     },
25459         
25460     applyBlacklists : function()
25461     {
25462         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25463         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25464         
25465         this.white = [];
25466         this.black = [];
25467         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25468             if (b.indexOf(tag) > -1) {
25469                 return;
25470             }
25471             this.white.push(tag);
25472             
25473         }, this);
25474         
25475         Roo.each(w, function(tag) {
25476             if (b.indexOf(tag) > -1) {
25477                 return;
25478             }
25479             if (this.white.indexOf(tag) > -1) {
25480                 return;
25481             }
25482             this.white.push(tag);
25483             
25484         }, this);
25485         
25486         
25487         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25488             if (w.indexOf(tag) > -1) {
25489                 return;
25490             }
25491             this.black.push(tag);
25492             
25493         }, this);
25494         
25495         Roo.each(b, function(tag) {
25496             if (w.indexOf(tag) > -1) {
25497                 return;
25498             }
25499             if (this.black.indexOf(tag) > -1) {
25500                 return;
25501             }
25502             this.black.push(tag);
25503             
25504         }, this);
25505         
25506         
25507         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25508         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25509         
25510         this.cwhite = [];
25511         this.cblack = [];
25512         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25513             if (b.indexOf(tag) > -1) {
25514                 return;
25515             }
25516             this.cwhite.push(tag);
25517             
25518         }, this);
25519         
25520         Roo.each(w, function(tag) {
25521             if (b.indexOf(tag) > -1) {
25522                 return;
25523             }
25524             if (this.cwhite.indexOf(tag) > -1) {
25525                 return;
25526             }
25527             this.cwhite.push(tag);
25528             
25529         }, this);
25530         
25531         
25532         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25533             if (w.indexOf(tag) > -1) {
25534                 return;
25535             }
25536             this.cblack.push(tag);
25537             
25538         }, this);
25539         
25540         Roo.each(b, function(tag) {
25541             if (w.indexOf(tag) > -1) {
25542                 return;
25543             }
25544             if (this.cblack.indexOf(tag) > -1) {
25545                 return;
25546             }
25547             this.cblack.push(tag);
25548             
25549         }, this);
25550     },
25551     
25552     setStylesheets : function(stylesheets)
25553     {
25554         if(typeof(stylesheets) == 'string'){
25555             Roo.get(this.iframe.contentDocument.head).createChild({
25556                 tag : 'link',
25557                 rel : 'stylesheet',
25558                 type : 'text/css',
25559                 href : stylesheets
25560             });
25561             
25562             return;
25563         }
25564         var _this = this;
25565      
25566         Roo.each(stylesheets, function(s) {
25567             if(!s.length){
25568                 return;
25569             }
25570             
25571             Roo.get(_this.iframe.contentDocument.head).createChild({
25572                 tag : 'link',
25573                 rel : 'stylesheet',
25574                 type : 'text/css',
25575                 href : s
25576             });
25577         });
25578
25579         
25580     },
25581     
25582     removeStylesheets : function()
25583     {
25584         var _this = this;
25585         
25586         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25587             s.remove();
25588         });
25589     },
25590     
25591     setStyle : function(style)
25592     {
25593         Roo.get(this.iframe.contentDocument.head).createChild({
25594             tag : 'style',
25595             type : 'text/css',
25596             html : style
25597         });
25598
25599         return;
25600     }
25601     
25602     // hide stuff that is not compatible
25603     /**
25604      * @event blur
25605      * @hide
25606      */
25607     /**
25608      * @event change
25609      * @hide
25610      */
25611     /**
25612      * @event focus
25613      * @hide
25614      */
25615     /**
25616      * @event specialkey
25617      * @hide
25618      */
25619     /**
25620      * @cfg {String} fieldClass @hide
25621      */
25622     /**
25623      * @cfg {String} focusClass @hide
25624      */
25625     /**
25626      * @cfg {String} autoCreate @hide
25627      */
25628     /**
25629      * @cfg {String} inputType @hide
25630      */
25631     /**
25632      * @cfg {String} invalidClass @hide
25633      */
25634     /**
25635      * @cfg {String} invalidText @hide
25636      */
25637     /**
25638      * @cfg {String} msgFx @hide
25639      */
25640     /**
25641      * @cfg {String} validateOnBlur @hide
25642      */
25643 });
25644
25645 Roo.HtmlEditorCore.white = [
25646         'area', 'br', 'img', 'input', 'hr', 'wbr',
25647         
25648        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25649        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25650        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25651        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25652        'table',   'ul',         'xmp', 
25653        
25654        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25655       'thead',   'tr', 
25656      
25657       'dir', 'menu', 'ol', 'ul', 'dl',
25658        
25659       'embed',  'object'
25660 ];
25661
25662
25663 Roo.HtmlEditorCore.black = [
25664     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25665         'applet', // 
25666         'base',   'basefont', 'bgsound', 'blink',  'body', 
25667         'frame',  'frameset', 'head',    'html',   'ilayer', 
25668         'iframe', 'layer',  'link',     'meta',    'object',   
25669         'script', 'style' ,'title',  'xml' // clean later..
25670 ];
25671 Roo.HtmlEditorCore.clean = [
25672     'script', 'style', 'title', 'xml'
25673 ];
25674 Roo.HtmlEditorCore.remove = [
25675     'font'
25676 ];
25677 // attributes..
25678
25679 Roo.HtmlEditorCore.ablack = [
25680     'on'
25681 ];
25682     
25683 Roo.HtmlEditorCore.aclean = [ 
25684     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25685 ];
25686
25687 // protocols..
25688 Roo.HtmlEditorCore.pwhite= [
25689         'http',  'https',  'mailto'
25690 ];
25691
25692 // white listed style attributes.
25693 Roo.HtmlEditorCore.cwhite= [
25694       //  'text-align', /// default is to allow most things..
25695       
25696          
25697 //        'font-size'//??
25698 ];
25699
25700 // black listed style attributes.
25701 Roo.HtmlEditorCore.cblack= [
25702       //  'font-size' -- this can be set by the project 
25703 ];
25704
25705
25706 Roo.HtmlEditorCore.swapCodes   =[ 
25707     [    8211, "--" ], 
25708     [    8212, "--" ], 
25709     [    8216,  "'" ],  
25710     [    8217, "'" ],  
25711     [    8220, '"' ],  
25712     [    8221, '"' ],  
25713     [    8226, "*" ],  
25714     [    8230, "..." ]
25715 ]; 
25716
25717     /*
25718  * - LGPL
25719  *
25720  * HtmlEditor
25721  * 
25722  */
25723
25724 /**
25725  * @class Roo.bootstrap.HtmlEditor
25726  * @extends Roo.bootstrap.TextArea
25727  * Bootstrap HtmlEditor class
25728
25729  * @constructor
25730  * Create a new HtmlEditor
25731  * @param {Object} config The config object
25732  */
25733
25734 Roo.bootstrap.HtmlEditor = function(config){
25735     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25736     if (!this.toolbars) {
25737         this.toolbars = [];
25738     }
25739     
25740     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25741     this.addEvents({
25742             /**
25743              * @event initialize
25744              * Fires when the editor is fully initialized (including the iframe)
25745              * @param {HtmlEditor} this
25746              */
25747             initialize: true,
25748             /**
25749              * @event activate
25750              * Fires when the editor is first receives the focus. Any insertion must wait
25751              * until after this event.
25752              * @param {HtmlEditor} this
25753              */
25754             activate: true,
25755              /**
25756              * @event beforesync
25757              * Fires before the textarea is updated with content from the editor iframe. Return false
25758              * to cancel the sync.
25759              * @param {HtmlEditor} this
25760              * @param {String} html
25761              */
25762             beforesync: true,
25763              /**
25764              * @event beforepush
25765              * Fires before the iframe editor is updated with content from the textarea. Return false
25766              * to cancel the push.
25767              * @param {HtmlEditor} this
25768              * @param {String} html
25769              */
25770             beforepush: true,
25771              /**
25772              * @event sync
25773              * Fires when the textarea is updated with content from the editor iframe.
25774              * @param {HtmlEditor} this
25775              * @param {String} html
25776              */
25777             sync: true,
25778              /**
25779              * @event push
25780              * Fires when the iframe editor is updated with content from the textarea.
25781              * @param {HtmlEditor} this
25782              * @param {String} html
25783              */
25784             push: true,
25785              /**
25786              * @event editmodechange
25787              * Fires when the editor switches edit modes
25788              * @param {HtmlEditor} this
25789              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25790              */
25791             editmodechange: true,
25792             /**
25793              * @event editorevent
25794              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25795              * @param {HtmlEditor} this
25796              */
25797             editorevent: true,
25798             /**
25799              * @event firstfocus
25800              * Fires when on first focus - needed by toolbars..
25801              * @param {HtmlEditor} this
25802              */
25803             firstfocus: true,
25804             /**
25805              * @event autosave
25806              * Auto save the htmlEditor value as a file into Events
25807              * @param {HtmlEditor} this
25808              */
25809             autosave: true,
25810             /**
25811              * @event savedpreview
25812              * preview the saved version of htmlEditor
25813              * @param {HtmlEditor} this
25814              */
25815             savedpreview: true
25816         });
25817 };
25818
25819
25820 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25821     
25822     
25823       /**
25824      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25825      */
25826     toolbars : false,
25827     
25828      /**
25829     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25830     */
25831     btns : [],
25832    
25833      /**
25834      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25835      *                        Roo.resizable.
25836      */
25837     resizable : false,
25838      /**
25839      * @cfg {Number} height (in pixels)
25840      */   
25841     height: 300,
25842    /**
25843      * @cfg {Number} width (in pixels)
25844      */   
25845     width: false,
25846     
25847     /**
25848      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25849      * 
25850      */
25851     stylesheets: false,
25852     
25853     // id of frame..
25854     frameId: false,
25855     
25856     // private properties
25857     validationEvent : false,
25858     deferHeight: true,
25859     initialized : false,
25860     activated : false,
25861     
25862     onFocus : Roo.emptyFn,
25863     iframePad:3,
25864     hideMode:'offsets',
25865     
25866     tbContainer : false,
25867     
25868     bodyCls : '',
25869     
25870     toolbarContainer :function() {
25871         return this.wrap.select('.x-html-editor-tb',true).first();
25872     },
25873
25874     /**
25875      * Protected method that will not generally be called directly. It
25876      * is called when the editor creates its toolbar. Override this method if you need to
25877      * add custom toolbar buttons.
25878      * @param {HtmlEditor} editor
25879      */
25880     createToolbar : function(){
25881         Roo.log('renewing');
25882         Roo.log("create toolbars");
25883         
25884         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25885         this.toolbars[0].render(this.toolbarContainer());
25886         
25887         return;
25888         
25889 //        if (!editor.toolbars || !editor.toolbars.length) {
25890 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25891 //        }
25892 //        
25893 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25894 //            editor.toolbars[i] = Roo.factory(
25895 //                    typeof(editor.toolbars[i]) == 'string' ?
25896 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25897 //                Roo.bootstrap.HtmlEditor);
25898 //            editor.toolbars[i].init(editor);
25899 //        }
25900     },
25901
25902      
25903     // private
25904     onRender : function(ct, position)
25905     {
25906        // Roo.log("Call onRender: " + this.xtype);
25907         var _t = this;
25908         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25909       
25910         this.wrap = this.inputEl().wrap({
25911             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25912         });
25913         
25914         this.editorcore.onRender(ct, position);
25915          
25916         if (this.resizable) {
25917             this.resizeEl = new Roo.Resizable(this.wrap, {
25918                 pinned : true,
25919                 wrap: true,
25920                 dynamic : true,
25921                 minHeight : this.height,
25922                 height: this.height,
25923                 handles : this.resizable,
25924                 width: this.width,
25925                 listeners : {
25926                     resize : function(r, w, h) {
25927                         _t.onResize(w,h); // -something
25928                     }
25929                 }
25930             });
25931             
25932         }
25933         this.createToolbar(this);
25934        
25935         
25936         if(!this.width && this.resizable){
25937             this.setSize(this.wrap.getSize());
25938         }
25939         if (this.resizeEl) {
25940             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25941             // should trigger onReize..
25942         }
25943         
25944     },
25945
25946     // private
25947     onResize : function(w, h)
25948     {
25949         Roo.log('resize: ' +w + ',' + h );
25950         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25951         var ew = false;
25952         var eh = false;
25953         
25954         if(this.inputEl() ){
25955             if(typeof w == 'number'){
25956                 var aw = w - this.wrap.getFrameWidth('lr');
25957                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25958                 ew = aw;
25959             }
25960             if(typeof h == 'number'){
25961                  var tbh = -11;  // fixme it needs to tool bar size!
25962                 for (var i =0; i < this.toolbars.length;i++) {
25963                     // fixme - ask toolbars for heights?
25964                     tbh += this.toolbars[i].el.getHeight();
25965                     //if (this.toolbars[i].footer) {
25966                     //    tbh += this.toolbars[i].footer.el.getHeight();
25967                     //}
25968                 }
25969               
25970                 
25971                 
25972                 
25973                 
25974                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25975                 ah -= 5; // knock a few pixes off for look..
25976                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25977                 var eh = ah;
25978             }
25979         }
25980         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25981         this.editorcore.onResize(ew,eh);
25982         
25983     },
25984
25985     /**
25986      * Toggles the editor between standard and source edit mode.
25987      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25988      */
25989     toggleSourceEdit : function(sourceEditMode)
25990     {
25991         this.editorcore.toggleSourceEdit(sourceEditMode);
25992         
25993         if(this.editorcore.sourceEditMode){
25994             Roo.log('editor - showing textarea');
25995             
25996 //            Roo.log('in');
25997 //            Roo.log(this.syncValue());
25998             this.syncValue();
25999             this.inputEl().removeClass(['hide', 'x-hidden']);
26000             this.inputEl().dom.removeAttribute('tabIndex');
26001             this.inputEl().focus();
26002         }else{
26003             Roo.log('editor - hiding textarea');
26004 //            Roo.log('out')
26005 //            Roo.log(this.pushValue()); 
26006             this.pushValue();
26007             
26008             this.inputEl().addClass(['hide', 'x-hidden']);
26009             this.inputEl().dom.setAttribute('tabIndex', -1);
26010             //this.deferFocus();
26011         }
26012          
26013         if(this.resizable){
26014             this.setSize(this.wrap.getSize());
26015         }
26016         
26017         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26018     },
26019  
26020     // private (for BoxComponent)
26021     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26022
26023     // private (for BoxComponent)
26024     getResizeEl : function(){
26025         return this.wrap;
26026     },
26027
26028     // private (for BoxComponent)
26029     getPositionEl : function(){
26030         return this.wrap;
26031     },
26032
26033     // private
26034     initEvents : function(){
26035         this.originalValue = this.getValue();
26036     },
26037
26038 //    /**
26039 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26040 //     * @method
26041 //     */
26042 //    markInvalid : Roo.emptyFn,
26043 //    /**
26044 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26045 //     * @method
26046 //     */
26047 //    clearInvalid : Roo.emptyFn,
26048
26049     setValue : function(v){
26050         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26051         this.editorcore.pushValue();
26052     },
26053
26054      
26055     // private
26056     deferFocus : function(){
26057         this.focus.defer(10, this);
26058     },
26059
26060     // doc'ed in Field
26061     focus : function(){
26062         this.editorcore.focus();
26063         
26064     },
26065       
26066
26067     // private
26068     onDestroy : function(){
26069         
26070         
26071         
26072         if(this.rendered){
26073             
26074             for (var i =0; i < this.toolbars.length;i++) {
26075                 // fixme - ask toolbars for heights?
26076                 this.toolbars[i].onDestroy();
26077             }
26078             
26079             this.wrap.dom.innerHTML = '';
26080             this.wrap.remove();
26081         }
26082     },
26083
26084     // private
26085     onFirstFocus : function(){
26086         //Roo.log("onFirstFocus");
26087         this.editorcore.onFirstFocus();
26088          for (var i =0; i < this.toolbars.length;i++) {
26089             this.toolbars[i].onFirstFocus();
26090         }
26091         
26092     },
26093     
26094     // private
26095     syncValue : function()
26096     {   
26097         this.editorcore.syncValue();
26098     },
26099     
26100     pushValue : function()
26101     {   
26102         this.editorcore.pushValue();
26103     }
26104      
26105     
26106     // hide stuff that is not compatible
26107     /**
26108      * @event blur
26109      * @hide
26110      */
26111     /**
26112      * @event change
26113      * @hide
26114      */
26115     /**
26116      * @event focus
26117      * @hide
26118      */
26119     /**
26120      * @event specialkey
26121      * @hide
26122      */
26123     /**
26124      * @cfg {String} fieldClass @hide
26125      */
26126     /**
26127      * @cfg {String} focusClass @hide
26128      */
26129     /**
26130      * @cfg {String} autoCreate @hide
26131      */
26132     /**
26133      * @cfg {String} inputType @hide
26134      */
26135      
26136     /**
26137      * @cfg {String} invalidText @hide
26138      */
26139     /**
26140      * @cfg {String} msgFx @hide
26141      */
26142     /**
26143      * @cfg {String} validateOnBlur @hide
26144      */
26145 });
26146  
26147     
26148    
26149    
26150    
26151       
26152 Roo.namespace('Roo.bootstrap.htmleditor');
26153 /**
26154  * @class Roo.bootstrap.HtmlEditorToolbar1
26155  * Basic Toolbar
26156  * 
26157  * @example
26158  * Usage:
26159  *
26160  new Roo.bootstrap.HtmlEditor({
26161     ....
26162     toolbars : [
26163         new Roo.bootstrap.HtmlEditorToolbar1({
26164             disable : { fonts: 1 , format: 1, ..., ... , ...],
26165             btns : [ .... ]
26166         })
26167     }
26168      
26169  * 
26170  * @cfg {Object} disable List of elements to disable..
26171  * @cfg {Array} btns List of additional buttons.
26172  * 
26173  * 
26174  * NEEDS Extra CSS? 
26175  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26176  */
26177  
26178 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26179 {
26180     
26181     Roo.apply(this, config);
26182     
26183     // default disabled, based on 'good practice'..
26184     this.disable = this.disable || {};
26185     Roo.applyIf(this.disable, {
26186         fontSize : true,
26187         colors : true,
26188         specialElements : true
26189     });
26190     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26191     
26192     this.editor = config.editor;
26193     this.editorcore = config.editor.editorcore;
26194     
26195     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26196     
26197     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26198     // dont call parent... till later.
26199 }
26200 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26201      
26202     bar : true,
26203     
26204     editor : false,
26205     editorcore : false,
26206     
26207     
26208     formats : [
26209         "p" ,  
26210         "h1","h2","h3","h4","h5","h6", 
26211         "pre", "code", 
26212         "abbr", "acronym", "address", "cite", "samp", "var",
26213         'div','span'
26214     ],
26215     
26216     onRender : function(ct, position)
26217     {
26218        // Roo.log("Call onRender: " + this.xtype);
26219         
26220        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26221        Roo.log(this.el);
26222        this.el.dom.style.marginBottom = '0';
26223        var _this = this;
26224        var editorcore = this.editorcore;
26225        var editor= this.editor;
26226        
26227        var children = [];
26228        var btn = function(id,cmd , toggle, handler, html){
26229        
26230             var  event = toggle ? 'toggle' : 'click';
26231        
26232             var a = {
26233                 size : 'sm',
26234                 xtype: 'Button',
26235                 xns: Roo.bootstrap,
26236                 //glyphicon : id,
26237                 fa: id,
26238                 cmd : id || cmd,
26239                 enableToggle:toggle !== false,
26240                 html : html || '',
26241                 pressed : toggle ? false : null,
26242                 listeners : {}
26243             };
26244             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26245                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26246             };
26247             children.push(a);
26248             return a;
26249        }
26250        
26251     //    var cb_box = function...
26252         
26253         var style = {
26254                 xtype: 'Button',
26255                 size : 'sm',
26256                 xns: Roo.bootstrap,
26257                 fa : 'font',
26258                 //html : 'submit'
26259                 menu : {
26260                     xtype: 'Menu',
26261                     xns: Roo.bootstrap,
26262                     items:  []
26263                 }
26264         };
26265         Roo.each(this.formats, function(f) {
26266             style.menu.items.push({
26267                 xtype :'MenuItem',
26268                 xns: Roo.bootstrap,
26269                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26270                 tagname : f,
26271                 listeners : {
26272                     click : function()
26273                     {
26274                         editorcore.insertTag(this.tagname);
26275                         editor.focus();
26276                     }
26277                 }
26278                 
26279             });
26280         });
26281         children.push(style);   
26282         
26283         btn('bold',false,true);
26284         btn('italic',false,true);
26285         btn('align-left', 'justifyleft',true);
26286         btn('align-center', 'justifycenter',true);
26287         btn('align-right' , 'justifyright',true);
26288         btn('link', false, false, function(btn) {
26289             //Roo.log("create link?");
26290             var url = prompt(this.createLinkText, this.defaultLinkValue);
26291             if(url && url != 'http:/'+'/'){
26292                 this.editorcore.relayCmd('createlink', url);
26293             }
26294         }),
26295         btn('list','insertunorderedlist',true);
26296         btn('pencil', false,true, function(btn){
26297                 Roo.log(this);
26298                 this.toggleSourceEdit(btn.pressed);
26299         });
26300         
26301         if (this.editor.btns.length > 0) {
26302             for (var i = 0; i<this.editor.btns.length; i++) {
26303                 children.push(this.editor.btns[i]);
26304             }
26305         }
26306         
26307         /*
26308         var cog = {
26309                 xtype: 'Button',
26310                 size : 'sm',
26311                 xns: Roo.bootstrap,
26312                 glyphicon : 'cog',
26313                 //html : 'submit'
26314                 menu : {
26315                     xtype: 'Menu',
26316                     xns: Roo.bootstrap,
26317                     items:  []
26318                 }
26319         };
26320         
26321         cog.menu.items.push({
26322             xtype :'MenuItem',
26323             xns: Roo.bootstrap,
26324             html : Clean styles,
26325             tagname : f,
26326             listeners : {
26327                 click : function()
26328                 {
26329                     editorcore.insertTag(this.tagname);
26330                     editor.focus();
26331                 }
26332             }
26333             
26334         });
26335        */
26336         
26337          
26338        this.xtype = 'NavSimplebar';
26339         
26340         for(var i=0;i< children.length;i++) {
26341             
26342             this.buttons.add(this.addxtypeChild(children[i]));
26343             
26344         }
26345         
26346         editor.on('editorevent', this.updateToolbar, this);
26347     },
26348     onBtnClick : function(id)
26349     {
26350        this.editorcore.relayCmd(id);
26351        this.editorcore.focus();
26352     },
26353     
26354     /**
26355      * Protected method that will not generally be called directly. It triggers
26356      * a toolbar update by reading the markup state of the current selection in the editor.
26357      */
26358     updateToolbar: function(){
26359
26360         if(!this.editorcore.activated){
26361             this.editor.onFirstFocus(); // is this neeed?
26362             return;
26363         }
26364
26365         var btns = this.buttons; 
26366         var doc = this.editorcore.doc;
26367         btns.get('bold').setActive(doc.queryCommandState('bold'));
26368         btns.get('italic').setActive(doc.queryCommandState('italic'));
26369         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26370         
26371         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26372         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26373         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26374         
26375         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26376         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26377          /*
26378         
26379         var ans = this.editorcore.getAllAncestors();
26380         if (this.formatCombo) {
26381             
26382             
26383             var store = this.formatCombo.store;
26384             this.formatCombo.setValue("");
26385             for (var i =0; i < ans.length;i++) {
26386                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26387                     // select it..
26388                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26389                     break;
26390                 }
26391             }
26392         }
26393         
26394         
26395         
26396         // hides menus... - so this cant be on a menu...
26397         Roo.bootstrap.MenuMgr.hideAll();
26398         */
26399         Roo.bootstrap.MenuMgr.hideAll();
26400         //this.editorsyncValue();
26401     },
26402     onFirstFocus: function() {
26403         this.buttons.each(function(item){
26404            item.enable();
26405         });
26406     },
26407     toggleSourceEdit : function(sourceEditMode){
26408         
26409           
26410         if(sourceEditMode){
26411             Roo.log("disabling buttons");
26412            this.buttons.each( function(item){
26413                 if(item.cmd != 'pencil'){
26414                     item.disable();
26415                 }
26416             });
26417           
26418         }else{
26419             Roo.log("enabling buttons");
26420             if(this.editorcore.initialized){
26421                 this.buttons.each( function(item){
26422                     item.enable();
26423                 });
26424             }
26425             
26426         }
26427         Roo.log("calling toggole on editor");
26428         // tell the editor that it's been pressed..
26429         this.editor.toggleSourceEdit(sourceEditMode);
26430        
26431     }
26432 });
26433
26434
26435
26436
26437  
26438 /*
26439  * - LGPL
26440  */
26441
26442 /**
26443  * @class Roo.bootstrap.Markdown
26444  * @extends Roo.bootstrap.TextArea
26445  * Bootstrap Showdown editable area
26446  * @cfg {string} content
26447  * 
26448  * @constructor
26449  * Create a new Showdown
26450  */
26451
26452 Roo.bootstrap.Markdown = function(config){
26453     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26454    
26455 };
26456
26457 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26458     
26459     editing :false,
26460     
26461     initEvents : function()
26462     {
26463         
26464         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26465         this.markdownEl = this.el.createChild({
26466             cls : 'roo-markdown-area'
26467         });
26468         this.inputEl().addClass('d-none');
26469         if (this.getValue() == '') {
26470             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26471             
26472         } else {
26473             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26474         }
26475         this.markdownEl.on('click', this.toggleTextEdit, this);
26476         this.on('blur', this.toggleTextEdit, this);
26477         this.on('specialkey', this.resizeTextArea, this);
26478     },
26479     
26480     toggleTextEdit : function()
26481     {
26482         var sh = this.markdownEl.getHeight();
26483         this.inputEl().addClass('d-none');
26484         this.markdownEl.addClass('d-none');
26485         if (!this.editing) {
26486             // show editor?
26487             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26488             this.inputEl().removeClass('d-none');
26489             this.inputEl().focus();
26490             this.editing = true;
26491             return;
26492         }
26493         // show showdown...
26494         this.updateMarkdown();
26495         this.markdownEl.removeClass('d-none');
26496         this.editing = false;
26497         return;
26498     },
26499     updateMarkdown : function()
26500     {
26501         if (this.getValue() == '') {
26502             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26503             return;
26504         }
26505  
26506         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26507     },
26508     
26509     resizeTextArea: function () {
26510         
26511         var sh = 100;
26512         Roo.log([sh, this.getValue().split("\n").length * 30]);
26513         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26514     },
26515     setValue : function(val)
26516     {
26517         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26518         if (!this.editing) {
26519             this.updateMarkdown();
26520         }
26521         
26522     },
26523     focus : function()
26524     {
26525         if (!this.editing) {
26526             this.toggleTextEdit();
26527         }
26528         
26529     }
26530
26531
26532 });
26533 /**
26534  * @class Roo.bootstrap.Table.AbstractSelectionModel
26535  * @extends Roo.util.Observable
26536  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26537  * implemented by descendant classes.  This class should not be directly instantiated.
26538  * @constructor
26539  */
26540 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26541     this.locked = false;
26542     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26543 };
26544
26545
26546 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26547     /** @ignore Called by the grid automatically. Do not call directly. */
26548     init : function(grid){
26549         this.grid = grid;
26550         this.initEvents();
26551     },
26552
26553     /**
26554      * Locks the selections.
26555      */
26556     lock : function(){
26557         this.locked = true;
26558     },
26559
26560     /**
26561      * Unlocks the selections.
26562      */
26563     unlock : function(){
26564         this.locked = false;
26565     },
26566
26567     /**
26568      * Returns true if the selections are locked.
26569      * @return {Boolean}
26570      */
26571     isLocked : function(){
26572         return this.locked;
26573     },
26574     
26575     
26576     initEvents : function ()
26577     {
26578         
26579     }
26580 });
26581 /**
26582  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26583  * @class Roo.bootstrap.Table.RowSelectionModel
26584  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26585  * It supports multiple selections and keyboard selection/navigation. 
26586  * @constructor
26587  * @param {Object} config
26588  */
26589
26590 Roo.bootstrap.Table.RowSelectionModel = function(config){
26591     Roo.apply(this, config);
26592     this.selections = new Roo.util.MixedCollection(false, function(o){
26593         return o.id;
26594     });
26595
26596     this.last = false;
26597     this.lastActive = false;
26598
26599     this.addEvents({
26600         /**
26601              * @event selectionchange
26602              * Fires when the selection changes
26603              * @param {SelectionModel} this
26604              */
26605             "selectionchange" : true,
26606         /**
26607              * @event afterselectionchange
26608              * Fires after the selection changes (eg. by key press or clicking)
26609              * @param {SelectionModel} this
26610              */
26611             "afterselectionchange" : true,
26612         /**
26613              * @event beforerowselect
26614              * Fires when a row is selected being selected, return false to cancel.
26615              * @param {SelectionModel} this
26616              * @param {Number} rowIndex The selected index
26617              * @param {Boolean} keepExisting False if other selections will be cleared
26618              */
26619             "beforerowselect" : true,
26620         /**
26621              * @event rowselect
26622              * Fires when a row is selected.
26623              * @param {SelectionModel} this
26624              * @param {Number} rowIndex The selected index
26625              * @param {Roo.data.Record} r The record
26626              */
26627             "rowselect" : true,
26628         /**
26629              * @event rowdeselect
26630              * Fires when a row is deselected.
26631              * @param {SelectionModel} this
26632              * @param {Number} rowIndex The selected index
26633              */
26634         "rowdeselect" : true
26635     });
26636     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26637     this.locked = false;
26638  };
26639
26640 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26641     /**
26642      * @cfg {Boolean} singleSelect
26643      * True to allow selection of only one row at a time (defaults to false)
26644      */
26645     singleSelect : false,
26646
26647     // private
26648     initEvents : function()
26649     {
26650
26651         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26652         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26653         //}else{ // allow click to work like normal
26654          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26655         //}
26656         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26657         this.grid.on("rowclick", this.handleMouseDown, this);
26658         
26659         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26660             "up" : function(e){
26661                 if(!e.shiftKey){
26662                     this.selectPrevious(e.shiftKey);
26663                 }else if(this.last !== false && this.lastActive !== false){
26664                     var last = this.last;
26665                     this.selectRange(this.last,  this.lastActive-1);
26666                     this.grid.getView().focusRow(this.lastActive);
26667                     if(last !== false){
26668                         this.last = last;
26669                     }
26670                 }else{
26671                     this.selectFirstRow();
26672                 }
26673                 this.fireEvent("afterselectionchange", this);
26674             },
26675             "down" : function(e){
26676                 if(!e.shiftKey){
26677                     this.selectNext(e.shiftKey);
26678                 }else if(this.last !== false && this.lastActive !== false){
26679                     var last = this.last;
26680                     this.selectRange(this.last,  this.lastActive+1);
26681                     this.grid.getView().focusRow(this.lastActive);
26682                     if(last !== false){
26683                         this.last = last;
26684                     }
26685                 }else{
26686                     this.selectFirstRow();
26687                 }
26688                 this.fireEvent("afterselectionchange", this);
26689             },
26690             scope: this
26691         });
26692         this.grid.store.on('load', function(){
26693             this.selections.clear();
26694         },this);
26695         /*
26696         var view = this.grid.view;
26697         view.on("refresh", this.onRefresh, this);
26698         view.on("rowupdated", this.onRowUpdated, this);
26699         view.on("rowremoved", this.onRemove, this);
26700         */
26701     },
26702
26703     // private
26704     onRefresh : function()
26705     {
26706         var ds = this.grid.store, i, v = this.grid.view;
26707         var s = this.selections;
26708         s.each(function(r){
26709             if((i = ds.indexOfId(r.id)) != -1){
26710                 v.onRowSelect(i);
26711             }else{
26712                 s.remove(r);
26713             }
26714         });
26715     },
26716
26717     // private
26718     onRemove : function(v, index, r){
26719         this.selections.remove(r);
26720     },
26721
26722     // private
26723     onRowUpdated : function(v, index, r){
26724         if(this.isSelected(r)){
26725             v.onRowSelect(index);
26726         }
26727     },
26728
26729     /**
26730      * Select records.
26731      * @param {Array} records The records to select
26732      * @param {Boolean} keepExisting (optional) True to keep existing selections
26733      */
26734     selectRecords : function(records, keepExisting)
26735     {
26736         if(!keepExisting){
26737             this.clearSelections();
26738         }
26739             var ds = this.grid.store;
26740         for(var i = 0, len = records.length; i < len; i++){
26741             this.selectRow(ds.indexOf(records[i]), true);
26742         }
26743     },
26744
26745     /**
26746      * Gets the number of selected rows.
26747      * @return {Number}
26748      */
26749     getCount : function(){
26750         return this.selections.length;
26751     },
26752
26753     /**
26754      * Selects the first row in the grid.
26755      */
26756     selectFirstRow : function(){
26757         this.selectRow(0);
26758     },
26759
26760     /**
26761      * Select the last row.
26762      * @param {Boolean} keepExisting (optional) True to keep existing selections
26763      */
26764     selectLastRow : function(keepExisting){
26765         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26766         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26767     },
26768
26769     /**
26770      * Selects the row immediately following the last selected row.
26771      * @param {Boolean} keepExisting (optional) True to keep existing selections
26772      */
26773     selectNext : function(keepExisting)
26774     {
26775             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26776             this.selectRow(this.last+1, keepExisting);
26777             this.grid.getView().focusRow(this.last);
26778         }
26779     },
26780
26781     /**
26782      * Selects the row that precedes the last selected row.
26783      * @param {Boolean} keepExisting (optional) True to keep existing selections
26784      */
26785     selectPrevious : function(keepExisting){
26786         if(this.last){
26787             this.selectRow(this.last-1, keepExisting);
26788             this.grid.getView().focusRow(this.last);
26789         }
26790     },
26791
26792     /**
26793      * Returns the selected records
26794      * @return {Array} Array of selected records
26795      */
26796     getSelections : function(){
26797         return [].concat(this.selections.items);
26798     },
26799
26800     /**
26801      * Returns the first selected record.
26802      * @return {Record}
26803      */
26804     getSelected : function(){
26805         return this.selections.itemAt(0);
26806     },
26807
26808
26809     /**
26810      * Clears all selections.
26811      */
26812     clearSelections : function(fast)
26813     {
26814         if(this.locked) {
26815             return;
26816         }
26817         if(fast !== true){
26818                 var ds = this.grid.store;
26819             var s = this.selections;
26820             s.each(function(r){
26821                 this.deselectRow(ds.indexOfId(r.id));
26822             }, this);
26823             s.clear();
26824         }else{
26825             this.selections.clear();
26826         }
26827         this.last = false;
26828     },
26829
26830
26831     /**
26832      * Selects all rows.
26833      */
26834     selectAll : function(){
26835         if(this.locked) {
26836             return;
26837         }
26838         this.selections.clear();
26839         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26840             this.selectRow(i, true);
26841         }
26842     },
26843
26844     /**
26845      * Returns True if there is a selection.
26846      * @return {Boolean}
26847      */
26848     hasSelection : function(){
26849         return this.selections.length > 0;
26850     },
26851
26852     /**
26853      * Returns True if the specified row is selected.
26854      * @param {Number/Record} record The record or index of the record to check
26855      * @return {Boolean}
26856      */
26857     isSelected : function(index){
26858             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26859         return (r && this.selections.key(r.id) ? true : false);
26860     },
26861
26862     /**
26863      * Returns True if the specified record id is selected.
26864      * @param {String} id The id of record to check
26865      * @return {Boolean}
26866      */
26867     isIdSelected : function(id){
26868         return (this.selections.key(id) ? true : false);
26869     },
26870
26871
26872     // private
26873     handleMouseDBClick : function(e, t){
26874         
26875     },
26876     // private
26877     handleMouseDown : function(e, t)
26878     {
26879             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26880         if(this.isLocked() || rowIndex < 0 ){
26881             return;
26882         };
26883         if(e.shiftKey && this.last !== false){
26884             var last = this.last;
26885             this.selectRange(last, rowIndex, e.ctrlKey);
26886             this.last = last; // reset the last
26887             t.focus();
26888     
26889         }else{
26890             var isSelected = this.isSelected(rowIndex);
26891             //Roo.log("select row:" + rowIndex);
26892             if(isSelected){
26893                 this.deselectRow(rowIndex);
26894             } else {
26895                         this.selectRow(rowIndex, true);
26896             }
26897     
26898             /*
26899                 if(e.button !== 0 && isSelected){
26900                 alert('rowIndex 2: ' + rowIndex);
26901                     view.focusRow(rowIndex);
26902                 }else if(e.ctrlKey && isSelected){
26903                     this.deselectRow(rowIndex);
26904                 }else if(!isSelected){
26905                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26906                     view.focusRow(rowIndex);
26907                 }
26908             */
26909         }
26910         this.fireEvent("afterselectionchange", this);
26911     },
26912     // private
26913     handleDragableRowClick :  function(grid, rowIndex, e) 
26914     {
26915         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26916             this.selectRow(rowIndex, false);
26917             grid.view.focusRow(rowIndex);
26918              this.fireEvent("afterselectionchange", this);
26919         }
26920     },
26921     
26922     /**
26923      * Selects multiple rows.
26924      * @param {Array} rows Array of the indexes of the row to select
26925      * @param {Boolean} keepExisting (optional) True to keep existing selections
26926      */
26927     selectRows : function(rows, keepExisting){
26928         if(!keepExisting){
26929             this.clearSelections();
26930         }
26931         for(var i = 0, len = rows.length; i < len; i++){
26932             this.selectRow(rows[i], true);
26933         }
26934     },
26935
26936     /**
26937      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26938      * @param {Number} startRow The index of the first row in the range
26939      * @param {Number} endRow The index of the last row in the range
26940      * @param {Boolean} keepExisting (optional) True to retain existing selections
26941      */
26942     selectRange : function(startRow, endRow, keepExisting){
26943         if(this.locked) {
26944             return;
26945         }
26946         if(!keepExisting){
26947             this.clearSelections();
26948         }
26949         if(startRow <= endRow){
26950             for(var i = startRow; i <= endRow; i++){
26951                 this.selectRow(i, true);
26952             }
26953         }else{
26954             for(var i = startRow; i >= endRow; i--){
26955                 this.selectRow(i, true);
26956             }
26957         }
26958     },
26959
26960     /**
26961      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26962      * @param {Number} startRow The index of the first row in the range
26963      * @param {Number} endRow The index of the last row in the range
26964      */
26965     deselectRange : function(startRow, endRow, preventViewNotify){
26966         if(this.locked) {
26967             return;
26968         }
26969         for(var i = startRow; i <= endRow; i++){
26970             this.deselectRow(i, preventViewNotify);
26971         }
26972     },
26973
26974     /**
26975      * Selects a row.
26976      * @param {Number} row The index of the row to select
26977      * @param {Boolean} keepExisting (optional) True to keep existing selections
26978      */
26979     selectRow : function(index, keepExisting, preventViewNotify)
26980     {
26981             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26982             return;
26983         }
26984         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26985             if(!keepExisting || this.singleSelect){
26986                 this.clearSelections();
26987             }
26988             
26989             var r = this.grid.store.getAt(index);
26990             //console.log('selectRow - record id :' + r.id);
26991             
26992             this.selections.add(r);
26993             this.last = this.lastActive = index;
26994             if(!preventViewNotify){
26995                 var proxy = new Roo.Element(
26996                                 this.grid.getRowDom(index)
26997                 );
26998                 proxy.addClass('bg-info info');
26999             }
27000             this.fireEvent("rowselect", this, index, r);
27001             this.fireEvent("selectionchange", this);
27002         }
27003     },
27004
27005     /**
27006      * Deselects a row.
27007      * @param {Number} row The index of the row to deselect
27008      */
27009     deselectRow : function(index, preventViewNotify)
27010     {
27011         if(this.locked) {
27012             return;
27013         }
27014         if(this.last == index){
27015             this.last = false;
27016         }
27017         if(this.lastActive == index){
27018             this.lastActive = false;
27019         }
27020         
27021         var r = this.grid.store.getAt(index);
27022         if (!r) {
27023             return;
27024         }
27025         
27026         this.selections.remove(r);
27027         //.console.log('deselectRow - record id :' + r.id);
27028         if(!preventViewNotify){
27029         
27030             var proxy = new Roo.Element(
27031                 this.grid.getRowDom(index)
27032             );
27033             proxy.removeClass('bg-info info');
27034         }
27035         this.fireEvent("rowdeselect", this, index);
27036         this.fireEvent("selectionchange", this);
27037     },
27038
27039     // private
27040     restoreLast : function(){
27041         if(this._last){
27042             this.last = this._last;
27043         }
27044     },
27045
27046     // private
27047     acceptsNav : function(row, col, cm){
27048         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27049     },
27050
27051     // private
27052     onEditorKey : function(field, e){
27053         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27054         if(k == e.TAB){
27055             e.stopEvent();
27056             ed.completeEdit();
27057             if(e.shiftKey){
27058                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27059             }else{
27060                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27061             }
27062         }else if(k == e.ENTER && !e.ctrlKey){
27063             e.stopEvent();
27064             ed.completeEdit();
27065             if(e.shiftKey){
27066                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27067             }else{
27068                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27069             }
27070         }else if(k == e.ESC){
27071             ed.cancelEdit();
27072         }
27073         if(newCell){
27074             g.startEditing(newCell[0], newCell[1]);
27075         }
27076     }
27077 });
27078 /*
27079  * Based on:
27080  * Ext JS Library 1.1.1
27081  * Copyright(c) 2006-2007, Ext JS, LLC.
27082  *
27083  * Originally Released Under LGPL - original licence link has changed is not relivant.
27084  *
27085  * Fork - LGPL
27086  * <script type="text/javascript">
27087  */
27088  
27089 /**
27090  * @class Roo.bootstrap.PagingToolbar
27091  * @extends Roo.bootstrap.NavSimplebar
27092  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27093  * @constructor
27094  * Create a new PagingToolbar
27095  * @param {Object} config The config object
27096  * @param {Roo.data.Store} store
27097  */
27098 Roo.bootstrap.PagingToolbar = function(config)
27099 {
27100     // old args format still supported... - xtype is prefered..
27101         // created from xtype...
27102     
27103     this.ds = config.dataSource;
27104     
27105     if (config.store && !this.ds) {
27106         this.store= Roo.factory(config.store, Roo.data);
27107         this.ds = this.store;
27108         this.ds.xmodule = this.xmodule || false;
27109     }
27110     
27111     this.toolbarItems = [];
27112     if (config.items) {
27113         this.toolbarItems = config.items;
27114     }
27115     
27116     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27117     
27118     this.cursor = 0;
27119     
27120     if (this.ds) { 
27121         this.bind(this.ds);
27122     }
27123     
27124     if (Roo.bootstrap.version == 4) {
27125         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27126     } else {
27127         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27128     }
27129     
27130 };
27131
27132 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27133     /**
27134      * @cfg {Roo.data.Store} dataSource
27135      * The underlying data store providing the paged data
27136      */
27137     /**
27138      * @cfg {String/HTMLElement/Element} container
27139      * container The id or element that will contain the toolbar
27140      */
27141     /**
27142      * @cfg {Boolean} displayInfo
27143      * True to display the displayMsg (defaults to false)
27144      */
27145     /**
27146      * @cfg {Number} pageSize
27147      * The number of records to display per page (defaults to 20)
27148      */
27149     pageSize: 20,
27150     /**
27151      * @cfg {String} displayMsg
27152      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27153      */
27154     displayMsg : 'Displaying {0} - {1} of {2}',
27155     /**
27156      * @cfg {String} emptyMsg
27157      * The message to display when no records are found (defaults to "No data to display")
27158      */
27159     emptyMsg : 'No data to display',
27160     /**
27161      * Customizable piece of the default paging text (defaults to "Page")
27162      * @type String
27163      */
27164     beforePageText : "Page",
27165     /**
27166      * Customizable piece of the default paging text (defaults to "of %0")
27167      * @type String
27168      */
27169     afterPageText : "of {0}",
27170     /**
27171      * Customizable piece of the default paging text (defaults to "First Page")
27172      * @type String
27173      */
27174     firstText : "First Page",
27175     /**
27176      * Customizable piece of the default paging text (defaults to "Previous Page")
27177      * @type String
27178      */
27179     prevText : "Previous Page",
27180     /**
27181      * Customizable piece of the default paging text (defaults to "Next Page")
27182      * @type String
27183      */
27184     nextText : "Next Page",
27185     /**
27186      * Customizable piece of the default paging text (defaults to "Last Page")
27187      * @type String
27188      */
27189     lastText : "Last Page",
27190     /**
27191      * Customizable piece of the default paging text (defaults to "Refresh")
27192      * @type String
27193      */
27194     refreshText : "Refresh",
27195
27196     buttons : false,
27197     // private
27198     onRender : function(ct, position) 
27199     {
27200         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27201         this.navgroup.parentId = this.id;
27202         this.navgroup.onRender(this.el, null);
27203         // add the buttons to the navgroup
27204         
27205         if(this.displayInfo){
27206             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27207             this.displayEl = this.el.select('.x-paging-info', true).first();
27208 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27209 //            this.displayEl = navel.el.select('span',true).first();
27210         }
27211         
27212         var _this = this;
27213         
27214         if(this.buttons){
27215             Roo.each(_this.buttons, function(e){ // this might need to use render????
27216                Roo.factory(e).render(_this.el);
27217             });
27218         }
27219             
27220         Roo.each(_this.toolbarItems, function(e) {
27221             _this.navgroup.addItem(e);
27222         });
27223         
27224         
27225         this.first = this.navgroup.addItem({
27226             tooltip: this.firstText,
27227             cls: "prev btn-outline-secondary",
27228             html : ' <i class="fa fa-step-backward"></i>',
27229             disabled: true,
27230             preventDefault: true,
27231             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27232         });
27233         
27234         this.prev =  this.navgroup.addItem({
27235             tooltip: this.prevText,
27236             cls: "prev btn-outline-secondary",
27237             html : ' <i class="fa fa-backward"></i>',
27238             disabled: true,
27239             preventDefault: true,
27240             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27241         });
27242     //this.addSeparator();
27243         
27244         
27245         var field = this.navgroup.addItem( {
27246             tagtype : 'span',
27247             cls : 'x-paging-position  btn-outline-secondary',
27248              disabled: true,
27249             html : this.beforePageText  +
27250                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27251                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27252          } ); //?? escaped?
27253         
27254         this.field = field.el.select('input', true).first();
27255         this.field.on("keydown", this.onPagingKeydown, this);
27256         this.field.on("focus", function(){this.dom.select();});
27257     
27258     
27259         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27260         //this.field.setHeight(18);
27261         //this.addSeparator();
27262         this.next = this.navgroup.addItem({
27263             tooltip: this.nextText,
27264             cls: "next btn-outline-secondary",
27265             html : ' <i class="fa fa-forward"></i>',
27266             disabled: true,
27267             preventDefault: true,
27268             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27269         });
27270         this.last = this.navgroup.addItem({
27271             tooltip: this.lastText,
27272             html : ' <i class="fa fa-step-forward"></i>',
27273             cls: "next btn-outline-secondary",
27274             disabled: true,
27275             preventDefault: true,
27276             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27277         });
27278     //this.addSeparator();
27279         this.loading = this.navgroup.addItem({
27280             tooltip: this.refreshText,
27281             cls: "btn-outline-secondary",
27282             html : ' <i class="fa fa-refresh"></i>',
27283             preventDefault: true,
27284             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27285         });
27286         
27287     },
27288
27289     // private
27290     updateInfo : function(){
27291         if(this.displayEl){
27292             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27293             var msg = count == 0 ?
27294                 this.emptyMsg :
27295                 String.format(
27296                     this.displayMsg,
27297                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27298                 );
27299             this.displayEl.update(msg);
27300         }
27301     },
27302
27303     // private
27304     onLoad : function(ds, r, o)
27305     {
27306         this.cursor = o.params && o.params.start ? o.params.start : 0;
27307         
27308         var d = this.getPageData(),
27309             ap = d.activePage,
27310             ps = d.pages;
27311         
27312         
27313         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27314         this.field.dom.value = ap;
27315         this.first.setDisabled(ap == 1);
27316         this.prev.setDisabled(ap == 1);
27317         this.next.setDisabled(ap == ps);
27318         this.last.setDisabled(ap == ps);
27319         this.loading.enable();
27320         this.updateInfo();
27321     },
27322
27323     // private
27324     getPageData : function(){
27325         var total = this.ds.getTotalCount();
27326         return {
27327             total : total,
27328             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27329             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27330         };
27331     },
27332
27333     // private
27334     onLoadError : function(){
27335         this.loading.enable();
27336     },
27337
27338     // private
27339     onPagingKeydown : function(e){
27340         var k = e.getKey();
27341         var d = this.getPageData();
27342         if(k == e.RETURN){
27343             var v = this.field.dom.value, pageNum;
27344             if(!v || isNaN(pageNum = parseInt(v, 10))){
27345                 this.field.dom.value = d.activePage;
27346                 return;
27347             }
27348             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27349             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27350             e.stopEvent();
27351         }
27352         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))
27353         {
27354           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27355           this.field.dom.value = pageNum;
27356           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27357           e.stopEvent();
27358         }
27359         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27360         {
27361           var v = this.field.dom.value, pageNum; 
27362           var increment = (e.shiftKey) ? 10 : 1;
27363           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27364                 increment *= -1;
27365           }
27366           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27367             this.field.dom.value = d.activePage;
27368             return;
27369           }
27370           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27371           {
27372             this.field.dom.value = parseInt(v, 10) + increment;
27373             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27374             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27375           }
27376           e.stopEvent();
27377         }
27378     },
27379
27380     // private
27381     beforeLoad : function(){
27382         if(this.loading){
27383             this.loading.disable();
27384         }
27385     },
27386
27387     // private
27388     onClick : function(which){
27389         
27390         var ds = this.ds;
27391         if (!ds) {
27392             return;
27393         }
27394         
27395         switch(which){
27396             case "first":
27397                 ds.load({params:{start: 0, limit: this.pageSize}});
27398             break;
27399             case "prev":
27400                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27401             break;
27402             case "next":
27403                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27404             break;
27405             case "last":
27406                 var total = ds.getTotalCount();
27407                 var extra = total % this.pageSize;
27408                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27409                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27410             break;
27411             case "refresh":
27412                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27413             break;
27414         }
27415     },
27416
27417     /**
27418      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27419      * @param {Roo.data.Store} store The data store to unbind
27420      */
27421     unbind : function(ds){
27422         ds.un("beforeload", this.beforeLoad, this);
27423         ds.un("load", this.onLoad, this);
27424         ds.un("loadexception", this.onLoadError, this);
27425         ds.un("remove", this.updateInfo, this);
27426         ds.un("add", this.updateInfo, this);
27427         this.ds = undefined;
27428     },
27429
27430     /**
27431      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27432      * @param {Roo.data.Store} store The data store to bind
27433      */
27434     bind : function(ds){
27435         ds.on("beforeload", this.beforeLoad, this);
27436         ds.on("load", this.onLoad, this);
27437         ds.on("loadexception", this.onLoadError, this);
27438         ds.on("remove", this.updateInfo, this);
27439         ds.on("add", this.updateInfo, this);
27440         this.ds = ds;
27441     }
27442 });/*
27443  * - LGPL
27444  *
27445  * element
27446  * 
27447  */
27448
27449 /**
27450  * @class Roo.bootstrap.MessageBar
27451  * @extends Roo.bootstrap.Component
27452  * Bootstrap MessageBar class
27453  * @cfg {String} html contents of the MessageBar
27454  * @cfg {String} weight (info | success | warning | danger) default info
27455  * @cfg {String} beforeClass insert the bar before the given class
27456  * @cfg {Boolean} closable (true | false) default false
27457  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27458  * 
27459  * @constructor
27460  * Create a new Element
27461  * @param {Object} config The config object
27462  */
27463
27464 Roo.bootstrap.MessageBar = function(config){
27465     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27466 };
27467
27468 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27469     
27470     html: '',
27471     weight: 'info',
27472     closable: false,
27473     fixed: false,
27474     beforeClass: 'bootstrap-sticky-wrap',
27475     
27476     getAutoCreate : function(){
27477         
27478         var cfg = {
27479             tag: 'div',
27480             cls: 'alert alert-dismissable alert-' + this.weight,
27481             cn: [
27482                 {
27483                     tag: 'span',
27484                     cls: 'message',
27485                     html: this.html || ''
27486                 }
27487             ]
27488         };
27489         
27490         if(this.fixed){
27491             cfg.cls += ' alert-messages-fixed';
27492         }
27493         
27494         if(this.closable){
27495             cfg.cn.push({
27496                 tag: 'button',
27497                 cls: 'close',
27498                 html: 'x'
27499             });
27500         }
27501         
27502         return cfg;
27503     },
27504     
27505     onRender : function(ct, position)
27506     {
27507         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27508         
27509         if(!this.el){
27510             var cfg = Roo.apply({},  this.getAutoCreate());
27511             cfg.id = Roo.id();
27512             
27513             if (this.cls) {
27514                 cfg.cls += ' ' + this.cls;
27515             }
27516             if (this.style) {
27517                 cfg.style = this.style;
27518             }
27519             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27520             
27521             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27522         }
27523         
27524         this.el.select('>button.close').on('click', this.hide, this);
27525         
27526     },
27527     
27528     show : function()
27529     {
27530         if (!this.rendered) {
27531             this.render();
27532         }
27533         
27534         this.el.show();
27535         
27536         this.fireEvent('show', this);
27537         
27538     },
27539     
27540     hide : function()
27541     {
27542         if (!this.rendered) {
27543             this.render();
27544         }
27545         
27546         this.el.hide();
27547         
27548         this.fireEvent('hide', this);
27549     },
27550     
27551     update : function()
27552     {
27553 //        var e = this.el.dom.firstChild;
27554 //        
27555 //        if(this.closable){
27556 //            e = e.nextSibling;
27557 //        }
27558 //        
27559 //        e.data = this.html || '';
27560
27561         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27562     }
27563    
27564 });
27565
27566  
27567
27568      /*
27569  * - LGPL
27570  *
27571  * Graph
27572  * 
27573  */
27574
27575
27576 /**
27577  * @class Roo.bootstrap.Graph
27578  * @extends Roo.bootstrap.Component
27579  * Bootstrap Graph class
27580 > Prameters
27581  -sm {number} sm 4
27582  -md {number} md 5
27583  @cfg {String} graphtype  bar | vbar | pie
27584  @cfg {number} g_x coodinator | centre x (pie)
27585  @cfg {number} g_y coodinator | centre y (pie)
27586  @cfg {number} g_r radius (pie)
27587  @cfg {number} g_height height of the chart (respected by all elements in the set)
27588  @cfg {number} g_width width of the chart (respected by all elements in the set)
27589  @cfg {Object} title The title of the chart
27590     
27591  -{Array}  values
27592  -opts (object) options for the chart 
27593      o {
27594      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27595      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27596      o vgutter (number)
27597      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.
27598      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27599      o to
27600      o stretch (boolean)
27601      o }
27602  -opts (object) options for the pie
27603      o{
27604      o cut
27605      o startAngle (number)
27606      o endAngle (number)
27607      } 
27608  *
27609  * @constructor
27610  * Create a new Input
27611  * @param {Object} config The config object
27612  */
27613
27614 Roo.bootstrap.Graph = function(config){
27615     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27616     
27617     this.addEvents({
27618         // img events
27619         /**
27620          * @event click
27621          * The img click event for the img.
27622          * @param {Roo.EventObject} e
27623          */
27624         "click" : true
27625     });
27626 };
27627
27628 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27629     
27630     sm: 4,
27631     md: 5,
27632     graphtype: 'bar',
27633     g_height: 250,
27634     g_width: 400,
27635     g_x: 50,
27636     g_y: 50,
27637     g_r: 30,
27638     opts:{
27639         //g_colors: this.colors,
27640         g_type: 'soft',
27641         g_gutter: '20%'
27642
27643     },
27644     title : false,
27645
27646     getAutoCreate : function(){
27647         
27648         var cfg = {
27649             tag: 'div',
27650             html : null
27651         };
27652         
27653         
27654         return  cfg;
27655     },
27656
27657     onRender : function(ct,position){
27658         
27659         
27660         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27661         
27662         if (typeof(Raphael) == 'undefined') {
27663             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27664             return;
27665         }
27666         
27667         this.raphael = Raphael(this.el.dom);
27668         
27669                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27670                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27671                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27672                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27673                 /*
27674                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27675                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27676                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27677                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27678                 
27679                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27680                 r.barchart(330, 10, 300, 220, data1);
27681                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27682                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27683                 */
27684                 
27685                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27686                 // r.barchart(30, 30, 560, 250,  xdata, {
27687                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27688                 //     axis : "0 0 1 1",
27689                 //     axisxlabels :  xdata
27690                 //     //yvalues : cols,
27691                    
27692                 // });
27693 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27694 //        
27695 //        this.load(null,xdata,{
27696 //                axis : "0 0 1 1",
27697 //                axisxlabels :  xdata
27698 //                });
27699
27700     },
27701
27702     load : function(graphtype,xdata,opts)
27703     {
27704         this.raphael.clear();
27705         if(!graphtype) {
27706             graphtype = this.graphtype;
27707         }
27708         if(!opts){
27709             opts = this.opts;
27710         }
27711         var r = this.raphael,
27712             fin = function () {
27713                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27714             },
27715             fout = function () {
27716                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27717             },
27718             pfin = function() {
27719                 this.sector.stop();
27720                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27721
27722                 if (this.label) {
27723                     this.label[0].stop();
27724                     this.label[0].attr({ r: 7.5 });
27725                     this.label[1].attr({ "font-weight": 800 });
27726                 }
27727             },
27728             pfout = function() {
27729                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27730
27731                 if (this.label) {
27732                     this.label[0].animate({ r: 5 }, 500, "bounce");
27733                     this.label[1].attr({ "font-weight": 400 });
27734                 }
27735             };
27736
27737         switch(graphtype){
27738             case 'bar':
27739                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27740                 break;
27741             case 'hbar':
27742                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27743                 break;
27744             case 'pie':
27745 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27746 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27747 //            
27748                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27749                 
27750                 break;
27751
27752         }
27753         
27754         if(this.title){
27755             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27756         }
27757         
27758     },
27759     
27760     setTitle: function(o)
27761     {
27762         this.title = o;
27763     },
27764     
27765     initEvents: function() {
27766         
27767         if(!this.href){
27768             this.el.on('click', this.onClick, this);
27769         }
27770     },
27771     
27772     onClick : function(e)
27773     {
27774         Roo.log('img onclick');
27775         this.fireEvent('click', this, e);
27776     }
27777    
27778 });
27779
27780  
27781 /*
27782  * - LGPL
27783  *
27784  * numberBox
27785  * 
27786  */
27787 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27788
27789 /**
27790  * @class Roo.bootstrap.dash.NumberBox
27791  * @extends Roo.bootstrap.Component
27792  * Bootstrap NumberBox class
27793  * @cfg {String} headline Box headline
27794  * @cfg {String} content Box content
27795  * @cfg {String} icon Box icon
27796  * @cfg {String} footer Footer text
27797  * @cfg {String} fhref Footer href
27798  * 
27799  * @constructor
27800  * Create a new NumberBox
27801  * @param {Object} config The config object
27802  */
27803
27804
27805 Roo.bootstrap.dash.NumberBox = function(config){
27806     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27807     
27808 };
27809
27810 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27811     
27812     headline : '',
27813     content : '',
27814     icon : '',
27815     footer : '',
27816     fhref : '',
27817     ficon : '',
27818     
27819     getAutoCreate : function(){
27820         
27821         var cfg = {
27822             tag : 'div',
27823             cls : 'small-box ',
27824             cn : [
27825                 {
27826                     tag : 'div',
27827                     cls : 'inner',
27828                     cn :[
27829                         {
27830                             tag : 'h3',
27831                             cls : 'roo-headline',
27832                             html : this.headline
27833                         },
27834                         {
27835                             tag : 'p',
27836                             cls : 'roo-content',
27837                             html : this.content
27838                         }
27839                     ]
27840                 }
27841             ]
27842         };
27843         
27844         if(this.icon){
27845             cfg.cn.push({
27846                 tag : 'div',
27847                 cls : 'icon',
27848                 cn :[
27849                     {
27850                         tag : 'i',
27851                         cls : 'ion ' + this.icon
27852                     }
27853                 ]
27854             });
27855         }
27856         
27857         if(this.footer){
27858             var footer = {
27859                 tag : 'a',
27860                 cls : 'small-box-footer',
27861                 href : this.fhref || '#',
27862                 html : this.footer
27863             };
27864             
27865             cfg.cn.push(footer);
27866             
27867         }
27868         
27869         return  cfg;
27870     },
27871
27872     onRender : function(ct,position){
27873         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27874
27875
27876        
27877                 
27878     },
27879
27880     setHeadline: function (value)
27881     {
27882         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27883     },
27884     
27885     setFooter: function (value, href)
27886     {
27887         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27888         
27889         if(href){
27890             this.el.select('a.small-box-footer',true).first().attr('href', href);
27891         }
27892         
27893     },
27894
27895     setContent: function (value)
27896     {
27897         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27898     },
27899
27900     initEvents: function() 
27901     {   
27902         
27903     }
27904     
27905 });
27906
27907  
27908 /*
27909  * - LGPL
27910  *
27911  * TabBox
27912  * 
27913  */
27914 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27915
27916 /**
27917  * @class Roo.bootstrap.dash.TabBox
27918  * @extends Roo.bootstrap.Component
27919  * Bootstrap TabBox class
27920  * @cfg {String} title Title of the TabBox
27921  * @cfg {String} icon Icon of the TabBox
27922  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27923  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27924  * 
27925  * @constructor
27926  * Create a new TabBox
27927  * @param {Object} config The config object
27928  */
27929
27930
27931 Roo.bootstrap.dash.TabBox = function(config){
27932     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27933     this.addEvents({
27934         // raw events
27935         /**
27936          * @event addpane
27937          * When a pane is added
27938          * @param {Roo.bootstrap.dash.TabPane} pane
27939          */
27940         "addpane" : true,
27941         /**
27942          * @event activatepane
27943          * When a pane is activated
27944          * @param {Roo.bootstrap.dash.TabPane} pane
27945          */
27946         "activatepane" : true
27947         
27948          
27949     });
27950     
27951     this.panes = [];
27952 };
27953
27954 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27955
27956     title : '',
27957     icon : false,
27958     showtabs : true,
27959     tabScrollable : false,
27960     
27961     getChildContainer : function()
27962     {
27963         return this.el.select('.tab-content', true).first();
27964     },
27965     
27966     getAutoCreate : function(){
27967         
27968         var header = {
27969             tag: 'li',
27970             cls: 'pull-left header',
27971             html: this.title,
27972             cn : []
27973         };
27974         
27975         if(this.icon){
27976             header.cn.push({
27977                 tag: 'i',
27978                 cls: 'fa ' + this.icon
27979             });
27980         }
27981         
27982         var h = {
27983             tag: 'ul',
27984             cls: 'nav nav-tabs pull-right',
27985             cn: [
27986                 header
27987             ]
27988         };
27989         
27990         if(this.tabScrollable){
27991             h = {
27992                 tag: 'div',
27993                 cls: 'tab-header',
27994                 cn: [
27995                     {
27996                         tag: 'ul',
27997                         cls: 'nav nav-tabs pull-right',
27998                         cn: [
27999                             header
28000                         ]
28001                     }
28002                 ]
28003             };
28004         }
28005         
28006         var cfg = {
28007             tag: 'div',
28008             cls: 'nav-tabs-custom',
28009             cn: [
28010                 h,
28011                 {
28012                     tag: 'div',
28013                     cls: 'tab-content no-padding',
28014                     cn: []
28015                 }
28016             ]
28017         };
28018
28019         return  cfg;
28020     },
28021     initEvents : function()
28022     {
28023         //Roo.log('add add pane handler');
28024         this.on('addpane', this.onAddPane, this);
28025     },
28026      /**
28027      * Updates the box title
28028      * @param {String} html to set the title to.
28029      */
28030     setTitle : function(value)
28031     {
28032         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28033     },
28034     onAddPane : function(pane)
28035     {
28036         this.panes.push(pane);
28037         //Roo.log('addpane');
28038         //Roo.log(pane);
28039         // tabs are rendere left to right..
28040         if(!this.showtabs){
28041             return;
28042         }
28043         
28044         var ctr = this.el.select('.nav-tabs', true).first();
28045          
28046          
28047         var existing = ctr.select('.nav-tab',true);
28048         var qty = existing.getCount();;
28049         
28050         
28051         var tab = ctr.createChild({
28052             tag : 'li',
28053             cls : 'nav-tab' + (qty ? '' : ' active'),
28054             cn : [
28055                 {
28056                     tag : 'a',
28057                     href:'#',
28058                     html : pane.title
28059                 }
28060             ]
28061         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28062         pane.tab = tab;
28063         
28064         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28065         if (!qty) {
28066             pane.el.addClass('active');
28067         }
28068         
28069                 
28070     },
28071     onTabClick : function(ev,un,ob,pane)
28072     {
28073         //Roo.log('tab - prev default');
28074         ev.preventDefault();
28075         
28076         
28077         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28078         pane.tab.addClass('active');
28079         //Roo.log(pane.title);
28080         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28081         // technically we should have a deactivate event.. but maybe add later.
28082         // and it should not de-activate the selected tab...
28083         this.fireEvent('activatepane', pane);
28084         pane.el.addClass('active');
28085         pane.fireEvent('activate');
28086         
28087         
28088     },
28089     
28090     getActivePane : function()
28091     {
28092         var r = false;
28093         Roo.each(this.panes, function(p) {
28094             if(p.el.hasClass('active')){
28095                 r = p;
28096                 return false;
28097             }
28098             
28099             return;
28100         });
28101         
28102         return r;
28103     }
28104     
28105     
28106 });
28107
28108  
28109 /*
28110  * - LGPL
28111  *
28112  * Tab pane
28113  * 
28114  */
28115 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28116 /**
28117  * @class Roo.bootstrap.TabPane
28118  * @extends Roo.bootstrap.Component
28119  * Bootstrap TabPane class
28120  * @cfg {Boolean} active (false | true) Default false
28121  * @cfg {String} title title of panel
28122
28123  * 
28124  * @constructor
28125  * Create a new TabPane
28126  * @param {Object} config The config object
28127  */
28128
28129 Roo.bootstrap.dash.TabPane = function(config){
28130     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28131     
28132     this.addEvents({
28133         // raw events
28134         /**
28135          * @event activate
28136          * When a pane is activated
28137          * @param {Roo.bootstrap.dash.TabPane} pane
28138          */
28139         "activate" : true
28140          
28141     });
28142 };
28143
28144 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28145     
28146     active : false,
28147     title : '',
28148     
28149     // the tabBox that this is attached to.
28150     tab : false,
28151      
28152     getAutoCreate : function() 
28153     {
28154         var cfg = {
28155             tag: 'div',
28156             cls: 'tab-pane'
28157         };
28158         
28159         if(this.active){
28160             cfg.cls += ' active';
28161         }
28162         
28163         return cfg;
28164     },
28165     initEvents  : function()
28166     {
28167         //Roo.log('trigger add pane handler');
28168         this.parent().fireEvent('addpane', this)
28169     },
28170     
28171      /**
28172      * Updates the tab title 
28173      * @param {String} html to set the title to.
28174      */
28175     setTitle: function(str)
28176     {
28177         if (!this.tab) {
28178             return;
28179         }
28180         this.title = str;
28181         this.tab.select('a', true).first().dom.innerHTML = str;
28182         
28183     }
28184     
28185     
28186     
28187 });
28188
28189  
28190
28191
28192  /*
28193  * - LGPL
28194  *
28195  * menu
28196  * 
28197  */
28198 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28199
28200 /**
28201  * @class Roo.bootstrap.menu.Menu
28202  * @extends Roo.bootstrap.Component
28203  * Bootstrap Menu class - container for Menu
28204  * @cfg {String} html Text of the menu
28205  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28206  * @cfg {String} icon Font awesome icon
28207  * @cfg {String} pos Menu align to (top | bottom) default bottom
28208  * 
28209  * 
28210  * @constructor
28211  * Create a new Menu
28212  * @param {Object} config The config object
28213  */
28214
28215
28216 Roo.bootstrap.menu.Menu = function(config){
28217     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28218     
28219     this.addEvents({
28220         /**
28221          * @event beforeshow
28222          * Fires before this menu is displayed
28223          * @param {Roo.bootstrap.menu.Menu} this
28224          */
28225         beforeshow : true,
28226         /**
28227          * @event beforehide
28228          * Fires before this menu is hidden
28229          * @param {Roo.bootstrap.menu.Menu} this
28230          */
28231         beforehide : true,
28232         /**
28233          * @event show
28234          * Fires after this menu is displayed
28235          * @param {Roo.bootstrap.menu.Menu} this
28236          */
28237         show : true,
28238         /**
28239          * @event hide
28240          * Fires after this menu is hidden
28241          * @param {Roo.bootstrap.menu.Menu} this
28242          */
28243         hide : true,
28244         /**
28245          * @event click
28246          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28247          * @param {Roo.bootstrap.menu.Menu} this
28248          * @param {Roo.EventObject} e
28249          */
28250         click : true
28251     });
28252     
28253 };
28254
28255 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28256     
28257     submenu : false,
28258     html : '',
28259     weight : 'default',
28260     icon : false,
28261     pos : 'bottom',
28262     
28263     
28264     getChildContainer : function() {
28265         if(this.isSubMenu){
28266             return this.el;
28267         }
28268         
28269         return this.el.select('ul.dropdown-menu', true).first();  
28270     },
28271     
28272     getAutoCreate : function()
28273     {
28274         var text = [
28275             {
28276                 tag : 'span',
28277                 cls : 'roo-menu-text',
28278                 html : this.html
28279             }
28280         ];
28281         
28282         if(this.icon){
28283             text.unshift({
28284                 tag : 'i',
28285                 cls : 'fa ' + this.icon
28286             })
28287         }
28288         
28289         
28290         var cfg = {
28291             tag : 'div',
28292             cls : 'btn-group',
28293             cn : [
28294                 {
28295                     tag : 'button',
28296                     cls : 'dropdown-button btn btn-' + this.weight,
28297                     cn : text
28298                 },
28299                 {
28300                     tag : 'button',
28301                     cls : 'dropdown-toggle btn btn-' + this.weight,
28302                     cn : [
28303                         {
28304                             tag : 'span',
28305                             cls : 'caret'
28306                         }
28307                     ]
28308                 },
28309                 {
28310                     tag : 'ul',
28311                     cls : 'dropdown-menu'
28312                 }
28313             ]
28314             
28315         };
28316         
28317         if(this.pos == 'top'){
28318             cfg.cls += ' dropup';
28319         }
28320         
28321         if(this.isSubMenu){
28322             cfg = {
28323                 tag : 'ul',
28324                 cls : 'dropdown-menu'
28325             }
28326         }
28327         
28328         return cfg;
28329     },
28330     
28331     onRender : function(ct, position)
28332     {
28333         this.isSubMenu = ct.hasClass('dropdown-submenu');
28334         
28335         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28336     },
28337     
28338     initEvents : function() 
28339     {
28340         if(this.isSubMenu){
28341             return;
28342         }
28343         
28344         this.hidden = true;
28345         
28346         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28347         this.triggerEl.on('click', this.onTriggerPress, this);
28348         
28349         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28350         this.buttonEl.on('click', this.onClick, this);
28351         
28352     },
28353     
28354     list : function()
28355     {
28356         if(this.isSubMenu){
28357             return this.el;
28358         }
28359         
28360         return this.el.select('ul.dropdown-menu', true).first();
28361     },
28362     
28363     onClick : function(e)
28364     {
28365         this.fireEvent("click", this, e);
28366     },
28367     
28368     onTriggerPress  : function(e)
28369     {   
28370         if (this.isVisible()) {
28371             this.hide();
28372         } else {
28373             this.show();
28374         }
28375     },
28376     
28377     isVisible : function(){
28378         return !this.hidden;
28379     },
28380     
28381     show : function()
28382     {
28383         this.fireEvent("beforeshow", this);
28384         
28385         this.hidden = false;
28386         this.el.addClass('open');
28387         
28388         Roo.get(document).on("mouseup", this.onMouseUp, this);
28389         
28390         this.fireEvent("show", this);
28391         
28392         
28393     },
28394     
28395     hide : function()
28396     {
28397         this.fireEvent("beforehide", this);
28398         
28399         this.hidden = true;
28400         this.el.removeClass('open');
28401         
28402         Roo.get(document).un("mouseup", this.onMouseUp);
28403         
28404         this.fireEvent("hide", this);
28405     },
28406     
28407     onMouseUp : function()
28408     {
28409         this.hide();
28410     }
28411     
28412 });
28413
28414  
28415  /*
28416  * - LGPL
28417  *
28418  * menu item
28419  * 
28420  */
28421 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28422
28423 /**
28424  * @class Roo.bootstrap.menu.Item
28425  * @extends Roo.bootstrap.Component
28426  * Bootstrap MenuItem class
28427  * @cfg {Boolean} submenu (true | false) default false
28428  * @cfg {String} html text of the item
28429  * @cfg {String} href the link
28430  * @cfg {Boolean} disable (true | false) default false
28431  * @cfg {Boolean} preventDefault (true | false) default true
28432  * @cfg {String} icon Font awesome icon
28433  * @cfg {String} pos Submenu align to (left | right) default right 
28434  * 
28435  * 
28436  * @constructor
28437  * Create a new Item
28438  * @param {Object} config The config object
28439  */
28440
28441
28442 Roo.bootstrap.menu.Item = function(config){
28443     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28444     this.addEvents({
28445         /**
28446          * @event mouseover
28447          * Fires when the mouse is hovering over this menu
28448          * @param {Roo.bootstrap.menu.Item} this
28449          * @param {Roo.EventObject} e
28450          */
28451         mouseover : true,
28452         /**
28453          * @event mouseout
28454          * Fires when the mouse exits this menu
28455          * @param {Roo.bootstrap.menu.Item} this
28456          * @param {Roo.EventObject} e
28457          */
28458         mouseout : true,
28459         // raw events
28460         /**
28461          * @event click
28462          * The raw click event for the entire grid.
28463          * @param {Roo.EventObject} e
28464          */
28465         click : true
28466     });
28467 };
28468
28469 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28470     
28471     submenu : false,
28472     href : '',
28473     html : '',
28474     preventDefault: true,
28475     disable : false,
28476     icon : false,
28477     pos : 'right',
28478     
28479     getAutoCreate : function()
28480     {
28481         var text = [
28482             {
28483                 tag : 'span',
28484                 cls : 'roo-menu-item-text',
28485                 html : this.html
28486             }
28487         ];
28488         
28489         if(this.icon){
28490             text.unshift({
28491                 tag : 'i',
28492                 cls : 'fa ' + this.icon
28493             })
28494         }
28495         
28496         var cfg = {
28497             tag : 'li',
28498             cn : [
28499                 {
28500                     tag : 'a',
28501                     href : this.href || '#',
28502                     cn : text
28503                 }
28504             ]
28505         };
28506         
28507         if(this.disable){
28508             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28509         }
28510         
28511         if(this.submenu){
28512             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28513             
28514             if(this.pos == 'left'){
28515                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28516             }
28517         }
28518         
28519         return cfg;
28520     },
28521     
28522     initEvents : function() 
28523     {
28524         this.el.on('mouseover', this.onMouseOver, this);
28525         this.el.on('mouseout', this.onMouseOut, this);
28526         
28527         this.el.select('a', true).first().on('click', this.onClick, this);
28528         
28529     },
28530     
28531     onClick : function(e)
28532     {
28533         if(this.preventDefault){
28534             e.preventDefault();
28535         }
28536         
28537         this.fireEvent("click", this, e);
28538     },
28539     
28540     onMouseOver : function(e)
28541     {
28542         if(this.submenu && this.pos == 'left'){
28543             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28544         }
28545         
28546         this.fireEvent("mouseover", this, e);
28547     },
28548     
28549     onMouseOut : function(e)
28550     {
28551         this.fireEvent("mouseout", this, e);
28552     }
28553 });
28554
28555  
28556
28557  /*
28558  * - LGPL
28559  *
28560  * menu separator
28561  * 
28562  */
28563 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28564
28565 /**
28566  * @class Roo.bootstrap.menu.Separator
28567  * @extends Roo.bootstrap.Component
28568  * Bootstrap Separator class
28569  * 
28570  * @constructor
28571  * Create a new Separator
28572  * @param {Object} config The config object
28573  */
28574
28575
28576 Roo.bootstrap.menu.Separator = function(config){
28577     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28578 };
28579
28580 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28581     
28582     getAutoCreate : function(){
28583         var cfg = {
28584             tag : 'li',
28585             cls: 'divider'
28586         };
28587         
28588         return cfg;
28589     }
28590    
28591 });
28592
28593  
28594
28595  /*
28596  * - LGPL
28597  *
28598  * Tooltip
28599  * 
28600  */
28601
28602 /**
28603  * @class Roo.bootstrap.Tooltip
28604  * Bootstrap Tooltip class
28605  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28606  * to determine which dom element triggers the tooltip.
28607  * 
28608  * It needs to add support for additional attributes like tooltip-position
28609  * 
28610  * @constructor
28611  * Create a new Toolti
28612  * @param {Object} config The config object
28613  */
28614
28615 Roo.bootstrap.Tooltip = function(config){
28616     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28617     
28618     this.alignment = Roo.bootstrap.Tooltip.alignment;
28619     
28620     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28621         this.alignment = config.alignment;
28622     }
28623     
28624 };
28625
28626 Roo.apply(Roo.bootstrap.Tooltip, {
28627     /**
28628      * @function init initialize tooltip monitoring.
28629      * @static
28630      */
28631     currentEl : false,
28632     currentTip : false,
28633     currentRegion : false,
28634     
28635     //  init : delay?
28636     
28637     init : function()
28638     {
28639         Roo.get(document).on('mouseover', this.enter ,this);
28640         Roo.get(document).on('mouseout', this.leave, this);
28641          
28642         
28643         this.currentTip = new Roo.bootstrap.Tooltip();
28644     },
28645     
28646     enter : function(ev)
28647     {
28648         var dom = ev.getTarget();
28649         
28650         //Roo.log(['enter',dom]);
28651         var el = Roo.fly(dom);
28652         if (this.currentEl) {
28653             //Roo.log(dom);
28654             //Roo.log(this.currentEl);
28655             //Roo.log(this.currentEl.contains(dom));
28656             if (this.currentEl == el) {
28657                 return;
28658             }
28659             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28660                 return;
28661             }
28662
28663         }
28664         
28665         if (this.currentTip.el) {
28666             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28667         }    
28668         //Roo.log(ev);
28669         
28670         if(!el || el.dom == document){
28671             return;
28672         }
28673         
28674         var bindEl = el;
28675         
28676         // you can not look for children, as if el is the body.. then everythign is the child..
28677         if (!el.attr('tooltip')) { //
28678             if (!el.select("[tooltip]").elements.length) {
28679                 return;
28680             }
28681             // is the mouse over this child...?
28682             bindEl = el.select("[tooltip]").first();
28683             var xy = ev.getXY();
28684             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28685                 //Roo.log("not in region.");
28686                 return;
28687             }
28688             //Roo.log("child element over..");
28689             
28690         }
28691         this.currentEl = bindEl;
28692         this.currentTip.bind(bindEl);
28693         this.currentRegion = Roo.lib.Region.getRegion(dom);
28694         this.currentTip.enter();
28695         
28696     },
28697     leave : function(ev)
28698     {
28699         var dom = ev.getTarget();
28700         //Roo.log(['leave',dom]);
28701         if (!this.currentEl) {
28702             return;
28703         }
28704         
28705         
28706         if (dom != this.currentEl.dom) {
28707             return;
28708         }
28709         var xy = ev.getXY();
28710         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28711             return;
28712         }
28713         // only activate leave if mouse cursor is outside... bounding box..
28714         
28715         
28716         
28717         
28718         if (this.currentTip) {
28719             this.currentTip.leave();
28720         }
28721         //Roo.log('clear currentEl');
28722         this.currentEl = false;
28723         
28724         
28725     },
28726     alignment : {
28727         'left' : ['r-l', [-2,0], 'right'],
28728         'right' : ['l-r', [2,0], 'left'],
28729         'bottom' : ['t-b', [0,2], 'top'],
28730         'top' : [ 'b-t', [0,-2], 'bottom']
28731     }
28732     
28733 });
28734
28735
28736 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28737     
28738     
28739     bindEl : false,
28740     
28741     delay : null, // can be { show : 300 , hide: 500}
28742     
28743     timeout : null,
28744     
28745     hoverState : null, //???
28746     
28747     placement : 'bottom', 
28748     
28749     alignment : false,
28750     
28751     getAutoCreate : function(){
28752     
28753         var cfg = {
28754            cls : 'tooltip',   
28755            role : 'tooltip',
28756            cn : [
28757                 {
28758                     cls : 'tooltip-arrow arrow'
28759                 },
28760                 {
28761                     cls : 'tooltip-inner'
28762                 }
28763            ]
28764         };
28765         
28766         return cfg;
28767     },
28768     bind : function(el)
28769     {
28770         this.bindEl = el;
28771     },
28772     
28773     initEvents : function()
28774     {
28775         this.arrowEl = this.el.select('.arrow', true).first();
28776         this.innerEl = this.el.select('.tooltip-inner', true).first();
28777     },
28778     
28779     enter : function () {
28780        
28781         if (this.timeout != null) {
28782             clearTimeout(this.timeout);
28783         }
28784         
28785         this.hoverState = 'in';
28786          //Roo.log("enter - show");
28787         if (!this.delay || !this.delay.show) {
28788             this.show();
28789             return;
28790         }
28791         var _t = this;
28792         this.timeout = setTimeout(function () {
28793             if (_t.hoverState == 'in') {
28794                 _t.show();
28795             }
28796         }, this.delay.show);
28797     },
28798     leave : function()
28799     {
28800         clearTimeout(this.timeout);
28801     
28802         this.hoverState = 'out';
28803          if (!this.delay || !this.delay.hide) {
28804             this.hide();
28805             return;
28806         }
28807        
28808         var _t = this;
28809         this.timeout = setTimeout(function () {
28810             //Roo.log("leave - timeout");
28811             
28812             if (_t.hoverState == 'out') {
28813                 _t.hide();
28814                 Roo.bootstrap.Tooltip.currentEl = false;
28815             }
28816         }, delay);
28817     },
28818     
28819     show : function (msg)
28820     {
28821         if (!this.el) {
28822             this.render(document.body);
28823         }
28824         // set content.
28825         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28826         
28827         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28828         
28829         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28830         
28831         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28832                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28833         
28834         var placement = typeof this.placement == 'function' ?
28835             this.placement.call(this, this.el, on_el) :
28836             this.placement;
28837             
28838         var autoToken = /\s?auto?\s?/i;
28839         var autoPlace = autoToken.test(placement);
28840         if (autoPlace) {
28841             placement = placement.replace(autoToken, '') || 'top';
28842         }
28843         
28844         //this.el.detach()
28845         //this.el.setXY([0,0]);
28846         this.el.show();
28847         //this.el.dom.style.display='block';
28848         
28849         //this.el.appendTo(on_el);
28850         
28851         var p = this.getPosition();
28852         var box = this.el.getBox();
28853         
28854         if (autoPlace) {
28855             // fixme..
28856         }
28857         
28858         var align = this.alignment[placement];
28859         
28860         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28861         
28862         if(placement == 'top' || placement == 'bottom'){
28863             if(xy[0] < 0){
28864                 placement = 'right';
28865             }
28866             
28867             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28868                 placement = 'left';
28869             }
28870             
28871             var scroll = Roo.select('body', true).first().getScroll();
28872             
28873             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28874                 placement = 'top';
28875             }
28876             
28877             align = this.alignment[placement];
28878             
28879             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28880             
28881         }
28882         
28883         this.el.alignTo(this.bindEl, align[0],align[1]);
28884         //var arrow = this.el.select('.arrow',true).first();
28885         //arrow.set(align[2], 
28886         
28887         this.el.addClass(placement);
28888         this.el.addClass("bs-tooltip-"+ placement);
28889         
28890         this.el.addClass('in fade show');
28891         
28892         this.hoverState = null;
28893         
28894         if (this.el.hasClass('fade')) {
28895             // fade it?
28896         }
28897         
28898         
28899         
28900         
28901         
28902     },
28903     hide : function()
28904     {
28905          
28906         if (!this.el) {
28907             return;
28908         }
28909         //this.el.setXY([0,0]);
28910         this.el.removeClass(['show', 'in']);
28911         //this.el.hide();
28912         
28913     }
28914     
28915 });
28916  
28917
28918  /*
28919  * - LGPL
28920  *
28921  * Location Picker
28922  * 
28923  */
28924
28925 /**
28926  * @class Roo.bootstrap.LocationPicker
28927  * @extends Roo.bootstrap.Component
28928  * Bootstrap LocationPicker class
28929  * @cfg {Number} latitude Position when init default 0
28930  * @cfg {Number} longitude Position when init default 0
28931  * @cfg {Number} zoom default 15
28932  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28933  * @cfg {Boolean} mapTypeControl default false
28934  * @cfg {Boolean} disableDoubleClickZoom default false
28935  * @cfg {Boolean} scrollwheel default true
28936  * @cfg {Boolean} streetViewControl default false
28937  * @cfg {Number} radius default 0
28938  * @cfg {String} locationName
28939  * @cfg {Boolean} draggable default true
28940  * @cfg {Boolean} enableAutocomplete default false
28941  * @cfg {Boolean} enableReverseGeocode default true
28942  * @cfg {String} markerTitle
28943  * 
28944  * @constructor
28945  * Create a new LocationPicker
28946  * @param {Object} config The config object
28947  */
28948
28949
28950 Roo.bootstrap.LocationPicker = function(config){
28951     
28952     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28953     
28954     this.addEvents({
28955         /**
28956          * @event initial
28957          * Fires when the picker initialized.
28958          * @param {Roo.bootstrap.LocationPicker} this
28959          * @param {Google Location} location
28960          */
28961         initial : true,
28962         /**
28963          * @event positionchanged
28964          * Fires when the picker position changed.
28965          * @param {Roo.bootstrap.LocationPicker} this
28966          * @param {Google Location} location
28967          */
28968         positionchanged : true,
28969         /**
28970          * @event resize
28971          * Fires when the map resize.
28972          * @param {Roo.bootstrap.LocationPicker} this
28973          */
28974         resize : true,
28975         /**
28976          * @event show
28977          * Fires when the map show.
28978          * @param {Roo.bootstrap.LocationPicker} this
28979          */
28980         show : true,
28981         /**
28982          * @event hide
28983          * Fires when the map hide.
28984          * @param {Roo.bootstrap.LocationPicker} this
28985          */
28986         hide : true,
28987         /**
28988          * @event mapClick
28989          * Fires when click the map.
28990          * @param {Roo.bootstrap.LocationPicker} this
28991          * @param {Map event} e
28992          */
28993         mapClick : true,
28994         /**
28995          * @event mapRightClick
28996          * Fires when right click the map.
28997          * @param {Roo.bootstrap.LocationPicker} this
28998          * @param {Map event} e
28999          */
29000         mapRightClick : true,
29001         /**
29002          * @event markerClick
29003          * Fires when click the marker.
29004          * @param {Roo.bootstrap.LocationPicker} this
29005          * @param {Map event} e
29006          */
29007         markerClick : true,
29008         /**
29009          * @event markerRightClick
29010          * Fires when right click the marker.
29011          * @param {Roo.bootstrap.LocationPicker} this
29012          * @param {Map event} e
29013          */
29014         markerRightClick : true,
29015         /**
29016          * @event OverlayViewDraw
29017          * Fires when OverlayView Draw
29018          * @param {Roo.bootstrap.LocationPicker} this
29019          */
29020         OverlayViewDraw : true,
29021         /**
29022          * @event OverlayViewOnAdd
29023          * Fires when OverlayView Draw
29024          * @param {Roo.bootstrap.LocationPicker} this
29025          */
29026         OverlayViewOnAdd : true,
29027         /**
29028          * @event OverlayViewOnRemove
29029          * Fires when OverlayView Draw
29030          * @param {Roo.bootstrap.LocationPicker} this
29031          */
29032         OverlayViewOnRemove : true,
29033         /**
29034          * @event OverlayViewShow
29035          * Fires when OverlayView Draw
29036          * @param {Roo.bootstrap.LocationPicker} this
29037          * @param {Pixel} cpx
29038          */
29039         OverlayViewShow : true,
29040         /**
29041          * @event OverlayViewHide
29042          * Fires when OverlayView Draw
29043          * @param {Roo.bootstrap.LocationPicker} this
29044          */
29045         OverlayViewHide : true,
29046         /**
29047          * @event loadexception
29048          * Fires when load google lib failed.
29049          * @param {Roo.bootstrap.LocationPicker} this
29050          */
29051         loadexception : true
29052     });
29053         
29054 };
29055
29056 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29057     
29058     gMapContext: false,
29059     
29060     latitude: 0,
29061     longitude: 0,
29062     zoom: 15,
29063     mapTypeId: false,
29064     mapTypeControl: false,
29065     disableDoubleClickZoom: false,
29066     scrollwheel: true,
29067     streetViewControl: false,
29068     radius: 0,
29069     locationName: '',
29070     draggable: true,
29071     enableAutocomplete: false,
29072     enableReverseGeocode: true,
29073     markerTitle: '',
29074     
29075     getAutoCreate: function()
29076     {
29077
29078         var cfg = {
29079             tag: 'div',
29080             cls: 'roo-location-picker'
29081         };
29082         
29083         return cfg
29084     },
29085     
29086     initEvents: function(ct, position)
29087     {       
29088         if(!this.el.getWidth() || this.isApplied()){
29089             return;
29090         }
29091         
29092         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29093         
29094         this.initial();
29095     },
29096     
29097     initial: function()
29098     {
29099         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29100             this.fireEvent('loadexception', this);
29101             return;
29102         }
29103         
29104         if(!this.mapTypeId){
29105             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29106         }
29107         
29108         this.gMapContext = this.GMapContext();
29109         
29110         this.initOverlayView();
29111         
29112         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29113         
29114         var _this = this;
29115                 
29116         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29117             _this.setPosition(_this.gMapContext.marker.position);
29118         });
29119         
29120         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29121             _this.fireEvent('mapClick', this, event);
29122             
29123         });
29124
29125         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29126             _this.fireEvent('mapRightClick', this, event);
29127             
29128         });
29129         
29130         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29131             _this.fireEvent('markerClick', this, event);
29132             
29133         });
29134
29135         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29136             _this.fireEvent('markerRightClick', this, event);
29137             
29138         });
29139         
29140         this.setPosition(this.gMapContext.location);
29141         
29142         this.fireEvent('initial', this, this.gMapContext.location);
29143     },
29144     
29145     initOverlayView: function()
29146     {
29147         var _this = this;
29148         
29149         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29150             
29151             draw: function()
29152             {
29153                 _this.fireEvent('OverlayViewDraw', _this);
29154             },
29155             
29156             onAdd: function()
29157             {
29158                 _this.fireEvent('OverlayViewOnAdd', _this);
29159             },
29160             
29161             onRemove: function()
29162             {
29163                 _this.fireEvent('OverlayViewOnRemove', _this);
29164             },
29165             
29166             show: function(cpx)
29167             {
29168                 _this.fireEvent('OverlayViewShow', _this, cpx);
29169             },
29170             
29171             hide: function()
29172             {
29173                 _this.fireEvent('OverlayViewHide', _this);
29174             }
29175             
29176         });
29177     },
29178     
29179     fromLatLngToContainerPixel: function(event)
29180     {
29181         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29182     },
29183     
29184     isApplied: function() 
29185     {
29186         return this.getGmapContext() == false ? false : true;
29187     },
29188     
29189     getGmapContext: function() 
29190     {
29191         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29192     },
29193     
29194     GMapContext: function() 
29195     {
29196         var position = new google.maps.LatLng(this.latitude, this.longitude);
29197         
29198         var _map = new google.maps.Map(this.el.dom, {
29199             center: position,
29200             zoom: this.zoom,
29201             mapTypeId: this.mapTypeId,
29202             mapTypeControl: this.mapTypeControl,
29203             disableDoubleClickZoom: this.disableDoubleClickZoom,
29204             scrollwheel: this.scrollwheel,
29205             streetViewControl: this.streetViewControl,
29206             locationName: this.locationName,
29207             draggable: this.draggable,
29208             enableAutocomplete: this.enableAutocomplete,
29209             enableReverseGeocode: this.enableReverseGeocode
29210         });
29211         
29212         var _marker = new google.maps.Marker({
29213             position: position,
29214             map: _map,
29215             title: this.markerTitle,
29216             draggable: this.draggable
29217         });
29218         
29219         return {
29220             map: _map,
29221             marker: _marker,
29222             circle: null,
29223             location: position,
29224             radius: this.radius,
29225             locationName: this.locationName,
29226             addressComponents: {
29227                 formatted_address: null,
29228                 addressLine1: null,
29229                 addressLine2: null,
29230                 streetName: null,
29231                 streetNumber: null,
29232                 city: null,
29233                 district: null,
29234                 state: null,
29235                 stateOrProvince: null
29236             },
29237             settings: this,
29238             domContainer: this.el.dom,
29239             geodecoder: new google.maps.Geocoder()
29240         };
29241     },
29242     
29243     drawCircle: function(center, radius, options) 
29244     {
29245         if (this.gMapContext.circle != null) {
29246             this.gMapContext.circle.setMap(null);
29247         }
29248         if (radius > 0) {
29249             radius *= 1;
29250             options = Roo.apply({}, options, {
29251                 strokeColor: "#0000FF",
29252                 strokeOpacity: .35,
29253                 strokeWeight: 2,
29254                 fillColor: "#0000FF",
29255                 fillOpacity: .2
29256             });
29257             
29258             options.map = this.gMapContext.map;
29259             options.radius = radius;
29260             options.center = center;
29261             this.gMapContext.circle = new google.maps.Circle(options);
29262             return this.gMapContext.circle;
29263         }
29264         
29265         return null;
29266     },
29267     
29268     setPosition: function(location) 
29269     {
29270         this.gMapContext.location = location;
29271         this.gMapContext.marker.setPosition(location);
29272         this.gMapContext.map.panTo(location);
29273         this.drawCircle(location, this.gMapContext.radius, {});
29274         
29275         var _this = this;
29276         
29277         if (this.gMapContext.settings.enableReverseGeocode) {
29278             this.gMapContext.geodecoder.geocode({
29279                 latLng: this.gMapContext.location
29280             }, function(results, status) {
29281                 
29282                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29283                     _this.gMapContext.locationName = results[0].formatted_address;
29284                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29285                     
29286                     _this.fireEvent('positionchanged', this, location);
29287                 }
29288             });
29289             
29290             return;
29291         }
29292         
29293         this.fireEvent('positionchanged', this, location);
29294     },
29295     
29296     resize: function()
29297     {
29298         google.maps.event.trigger(this.gMapContext.map, "resize");
29299         
29300         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29301         
29302         this.fireEvent('resize', this);
29303     },
29304     
29305     setPositionByLatLng: function(latitude, longitude)
29306     {
29307         this.setPosition(new google.maps.LatLng(latitude, longitude));
29308     },
29309     
29310     getCurrentPosition: function() 
29311     {
29312         return {
29313             latitude: this.gMapContext.location.lat(),
29314             longitude: this.gMapContext.location.lng()
29315         };
29316     },
29317     
29318     getAddressName: function() 
29319     {
29320         return this.gMapContext.locationName;
29321     },
29322     
29323     getAddressComponents: function() 
29324     {
29325         return this.gMapContext.addressComponents;
29326     },
29327     
29328     address_component_from_google_geocode: function(address_components) 
29329     {
29330         var result = {};
29331         
29332         for (var i = 0; i < address_components.length; i++) {
29333             var component = address_components[i];
29334             if (component.types.indexOf("postal_code") >= 0) {
29335                 result.postalCode = component.short_name;
29336             } else if (component.types.indexOf("street_number") >= 0) {
29337                 result.streetNumber = component.short_name;
29338             } else if (component.types.indexOf("route") >= 0) {
29339                 result.streetName = component.short_name;
29340             } else if (component.types.indexOf("neighborhood") >= 0) {
29341                 result.city = component.short_name;
29342             } else if (component.types.indexOf("locality") >= 0) {
29343                 result.city = component.short_name;
29344             } else if (component.types.indexOf("sublocality") >= 0) {
29345                 result.district = component.short_name;
29346             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29347                 result.stateOrProvince = component.short_name;
29348             } else if (component.types.indexOf("country") >= 0) {
29349                 result.country = component.short_name;
29350             }
29351         }
29352         
29353         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29354         result.addressLine2 = "";
29355         return result;
29356     },
29357     
29358     setZoomLevel: function(zoom)
29359     {
29360         this.gMapContext.map.setZoom(zoom);
29361     },
29362     
29363     show: function()
29364     {
29365         if(!this.el){
29366             return;
29367         }
29368         
29369         this.el.show();
29370         
29371         this.resize();
29372         
29373         this.fireEvent('show', this);
29374     },
29375     
29376     hide: function()
29377     {
29378         if(!this.el){
29379             return;
29380         }
29381         
29382         this.el.hide();
29383         
29384         this.fireEvent('hide', this);
29385     }
29386     
29387 });
29388
29389 Roo.apply(Roo.bootstrap.LocationPicker, {
29390     
29391     OverlayView : function(map, options)
29392     {
29393         options = options || {};
29394         
29395         this.setMap(map);
29396     }
29397     
29398     
29399 });/**
29400  * @class Roo.bootstrap.Alert
29401  * @extends Roo.bootstrap.Component
29402  * Bootstrap Alert class - shows an alert area box
29403  * eg
29404  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29405   Enter a valid email address
29406 </div>
29407  * @licence LGPL
29408  * @cfg {String} title The title of alert
29409  * @cfg {String} html The content of alert
29410  * @cfg {String} weight (  success | info | warning | danger )
29411  * @cfg {String} faicon font-awesomeicon
29412  * 
29413  * @constructor
29414  * Create a new alert
29415  * @param {Object} config The config object
29416  */
29417
29418
29419 Roo.bootstrap.Alert = function(config){
29420     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29421     
29422 };
29423
29424 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29425     
29426     title: '',
29427     html: '',
29428     weight: false,
29429     faicon: false,
29430     
29431     getAutoCreate : function()
29432     {
29433         
29434         var cfg = {
29435             tag : 'div',
29436             cls : 'alert',
29437             cn : [
29438                 {
29439                     tag : 'i',
29440                     cls : 'roo-alert-icon'
29441                     
29442                 },
29443                 {
29444                     tag : 'b',
29445                     cls : 'roo-alert-title',
29446                     html : this.title
29447                 },
29448                 {
29449                     tag : 'span',
29450                     cls : 'roo-alert-text',
29451                     html : this.html
29452                 }
29453             ]
29454         };
29455         
29456         if(this.faicon){
29457             cfg.cn[0].cls += ' fa ' + this.faicon;
29458         }
29459         
29460         if(this.weight){
29461             cfg.cls += ' alert-' + this.weight;
29462         }
29463         
29464         return cfg;
29465     },
29466     
29467     initEvents: function() 
29468     {
29469         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29470     },
29471     
29472     setTitle : function(str)
29473     {
29474         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29475     },
29476     
29477     setText : function(str)
29478     {
29479         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29480     },
29481     
29482     setWeight : function(weight)
29483     {
29484         if(this.weight){
29485             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29486         }
29487         
29488         this.weight = weight;
29489         
29490         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29491     },
29492     
29493     setIcon : function(icon)
29494     {
29495         if(this.faicon){
29496             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29497         }
29498         
29499         this.faicon = icon;
29500         
29501         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29502     },
29503     
29504     hide: function() 
29505     {
29506         this.el.hide();   
29507     },
29508     
29509     show: function() 
29510     {  
29511         this.el.show();   
29512     }
29513     
29514 });
29515
29516  
29517 /*
29518 * Licence: LGPL
29519 */
29520
29521 /**
29522  * @class Roo.bootstrap.UploadCropbox
29523  * @extends Roo.bootstrap.Component
29524  * Bootstrap UploadCropbox class
29525  * @cfg {String} emptyText show when image has been loaded
29526  * @cfg {String} rotateNotify show when image too small to rotate
29527  * @cfg {Number} errorTimeout default 3000
29528  * @cfg {Number} minWidth default 300
29529  * @cfg {Number} minHeight default 300
29530  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29531  * @cfg {Boolean} isDocument (true|false) default false
29532  * @cfg {String} url action url
29533  * @cfg {String} paramName default 'imageUpload'
29534  * @cfg {String} method default POST
29535  * @cfg {Boolean} loadMask (true|false) default true
29536  * @cfg {Boolean} loadingText default 'Loading...'
29537  * 
29538  * @constructor
29539  * Create a new UploadCropbox
29540  * @param {Object} config The config object
29541  */
29542
29543 Roo.bootstrap.UploadCropbox = function(config){
29544     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29545     
29546     this.addEvents({
29547         /**
29548          * @event beforeselectfile
29549          * Fire before select file
29550          * @param {Roo.bootstrap.UploadCropbox} this
29551          */
29552         "beforeselectfile" : true,
29553         /**
29554          * @event initial
29555          * Fire after initEvent
29556          * @param {Roo.bootstrap.UploadCropbox} this
29557          */
29558         "initial" : true,
29559         /**
29560          * @event crop
29561          * Fire after initEvent
29562          * @param {Roo.bootstrap.UploadCropbox} this
29563          * @param {String} data
29564          */
29565         "crop" : true,
29566         /**
29567          * @event prepare
29568          * Fire when preparing the file data
29569          * @param {Roo.bootstrap.UploadCropbox} this
29570          * @param {Object} file
29571          */
29572         "prepare" : true,
29573         /**
29574          * @event exception
29575          * Fire when get exception
29576          * @param {Roo.bootstrap.UploadCropbox} this
29577          * @param {XMLHttpRequest} xhr
29578          */
29579         "exception" : true,
29580         /**
29581          * @event beforeloadcanvas
29582          * Fire before load the canvas
29583          * @param {Roo.bootstrap.UploadCropbox} this
29584          * @param {String} src
29585          */
29586         "beforeloadcanvas" : true,
29587         /**
29588          * @event trash
29589          * Fire when trash image
29590          * @param {Roo.bootstrap.UploadCropbox} this
29591          */
29592         "trash" : true,
29593         /**
29594          * @event download
29595          * Fire when download the image
29596          * @param {Roo.bootstrap.UploadCropbox} this
29597          */
29598         "download" : true,
29599         /**
29600          * @event footerbuttonclick
29601          * Fire when footerbuttonclick
29602          * @param {Roo.bootstrap.UploadCropbox} this
29603          * @param {String} type
29604          */
29605         "footerbuttonclick" : true,
29606         /**
29607          * @event resize
29608          * Fire when resize
29609          * @param {Roo.bootstrap.UploadCropbox} this
29610          */
29611         "resize" : true,
29612         /**
29613          * @event rotate
29614          * Fire when rotate the image
29615          * @param {Roo.bootstrap.UploadCropbox} this
29616          * @param {String} pos
29617          */
29618         "rotate" : true,
29619         /**
29620          * @event inspect
29621          * Fire when inspect the file
29622          * @param {Roo.bootstrap.UploadCropbox} this
29623          * @param {Object} file
29624          */
29625         "inspect" : true,
29626         /**
29627          * @event upload
29628          * Fire when xhr upload the file
29629          * @param {Roo.bootstrap.UploadCropbox} this
29630          * @param {Object} data
29631          */
29632         "upload" : true,
29633         /**
29634          * @event arrange
29635          * Fire when arrange the file data
29636          * @param {Roo.bootstrap.UploadCropbox} this
29637          * @param {Object} formData
29638          */
29639         "arrange" : true
29640     });
29641     
29642     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29643 };
29644
29645 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29646     
29647     emptyText : 'Click to upload image',
29648     rotateNotify : 'Image is too small to rotate',
29649     errorTimeout : 3000,
29650     scale : 0,
29651     baseScale : 1,
29652     rotate : 0,
29653     dragable : false,
29654     pinching : false,
29655     mouseX : 0,
29656     mouseY : 0,
29657     cropData : false,
29658     minWidth : 300,
29659     minHeight : 300,
29660     file : false,
29661     exif : {},
29662     baseRotate : 1,
29663     cropType : 'image/jpeg',
29664     buttons : false,
29665     canvasLoaded : false,
29666     isDocument : false,
29667     method : 'POST',
29668     paramName : 'imageUpload',
29669     loadMask : true,
29670     loadingText : 'Loading...',
29671     maskEl : false,
29672     
29673     getAutoCreate : function()
29674     {
29675         var cfg = {
29676             tag : 'div',
29677             cls : 'roo-upload-cropbox',
29678             cn : [
29679                 {
29680                     tag : 'input',
29681                     cls : 'roo-upload-cropbox-selector',
29682                     type : 'file'
29683                 },
29684                 {
29685                     tag : 'div',
29686                     cls : 'roo-upload-cropbox-body',
29687                     style : 'cursor:pointer',
29688                     cn : [
29689                         {
29690                             tag : 'div',
29691                             cls : 'roo-upload-cropbox-preview'
29692                         },
29693                         {
29694                             tag : 'div',
29695                             cls : 'roo-upload-cropbox-thumb'
29696                         },
29697                         {
29698                             tag : 'div',
29699                             cls : 'roo-upload-cropbox-empty-notify',
29700                             html : this.emptyText
29701                         },
29702                         {
29703                             tag : 'div',
29704                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29705                             html : this.rotateNotify
29706                         }
29707                     ]
29708                 },
29709                 {
29710                     tag : 'div',
29711                     cls : 'roo-upload-cropbox-footer',
29712                     cn : {
29713                         tag : 'div',
29714                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29715                         cn : []
29716                     }
29717                 }
29718             ]
29719         };
29720         
29721         return cfg;
29722     },
29723     
29724     onRender : function(ct, position)
29725     {
29726         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29727         
29728         if (this.buttons.length) {
29729             
29730             Roo.each(this.buttons, function(bb) {
29731                 
29732                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29733                 
29734                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29735                 
29736             }, this);
29737         }
29738         
29739         if(this.loadMask){
29740             this.maskEl = this.el;
29741         }
29742     },
29743     
29744     initEvents : function()
29745     {
29746         this.urlAPI = (window.createObjectURL && window) || 
29747                                 (window.URL && URL.revokeObjectURL && URL) || 
29748                                 (window.webkitURL && webkitURL);
29749                         
29750         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29751         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29752         
29753         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29754         this.selectorEl.hide();
29755         
29756         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29757         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29758         
29759         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29760         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29761         this.thumbEl.hide();
29762         
29763         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29764         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29765         
29766         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29767         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29768         this.errorEl.hide();
29769         
29770         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29771         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29772         this.footerEl.hide();
29773         
29774         this.setThumbBoxSize();
29775         
29776         this.bind();
29777         
29778         this.resize();
29779         
29780         this.fireEvent('initial', this);
29781     },
29782
29783     bind : function()
29784     {
29785         var _this = this;
29786         
29787         window.addEventListener("resize", function() { _this.resize(); } );
29788         
29789         this.bodyEl.on('click', this.beforeSelectFile, this);
29790         
29791         if(Roo.isTouch){
29792             this.bodyEl.on('touchstart', this.onTouchStart, this);
29793             this.bodyEl.on('touchmove', this.onTouchMove, this);
29794             this.bodyEl.on('touchend', this.onTouchEnd, this);
29795         }
29796         
29797         if(!Roo.isTouch){
29798             this.bodyEl.on('mousedown', this.onMouseDown, this);
29799             this.bodyEl.on('mousemove', this.onMouseMove, this);
29800             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29801             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29802             Roo.get(document).on('mouseup', this.onMouseUp, this);
29803         }
29804         
29805         this.selectorEl.on('change', this.onFileSelected, this);
29806     },
29807     
29808     reset : function()
29809     {    
29810         this.scale = 0;
29811         this.baseScale = 1;
29812         this.rotate = 0;
29813         this.baseRotate = 1;
29814         this.dragable = false;
29815         this.pinching = false;
29816         this.mouseX = 0;
29817         this.mouseY = 0;
29818         this.cropData = false;
29819         this.notifyEl.dom.innerHTML = this.emptyText;
29820         
29821         this.selectorEl.dom.value = '';
29822         
29823     },
29824     
29825     resize : function()
29826     {
29827         if(this.fireEvent('resize', this) != false){
29828             this.setThumbBoxPosition();
29829             this.setCanvasPosition();
29830         }
29831     },
29832     
29833     onFooterButtonClick : function(e, el, o, type)
29834     {
29835         switch (type) {
29836             case 'rotate-left' :
29837                 this.onRotateLeft(e);
29838                 break;
29839             case 'rotate-right' :
29840                 this.onRotateRight(e);
29841                 break;
29842             case 'picture' :
29843                 this.beforeSelectFile(e);
29844                 break;
29845             case 'trash' :
29846                 this.trash(e);
29847                 break;
29848             case 'crop' :
29849                 this.crop(e);
29850                 break;
29851             case 'download' :
29852                 this.download(e);
29853                 break;
29854             default :
29855                 break;
29856         }
29857         
29858         this.fireEvent('footerbuttonclick', this, type);
29859     },
29860     
29861     beforeSelectFile : function(e)
29862     {
29863         e.preventDefault();
29864         
29865         if(this.fireEvent('beforeselectfile', this) != false){
29866             this.selectorEl.dom.click();
29867         }
29868     },
29869     
29870     onFileSelected : function(e)
29871     {
29872         e.preventDefault();
29873         
29874         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29875             return;
29876         }
29877         
29878         var file = this.selectorEl.dom.files[0];
29879         
29880         if(this.fireEvent('inspect', this, file) != false){
29881             this.prepare(file);
29882         }
29883         
29884     },
29885     
29886     trash : function(e)
29887     {
29888         this.fireEvent('trash', this);
29889     },
29890     
29891     download : function(e)
29892     {
29893         this.fireEvent('download', this);
29894     },
29895     
29896     loadCanvas : function(src)
29897     {   
29898         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29899             
29900             this.reset();
29901             
29902             this.imageEl = document.createElement('img');
29903             
29904             var _this = this;
29905             
29906             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29907             
29908             this.imageEl.src = src;
29909         }
29910     },
29911     
29912     onLoadCanvas : function()
29913     {   
29914         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29915         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29916         
29917         this.bodyEl.un('click', this.beforeSelectFile, this);
29918         
29919         this.notifyEl.hide();
29920         this.thumbEl.show();
29921         this.footerEl.show();
29922         
29923         this.baseRotateLevel();
29924         
29925         if(this.isDocument){
29926             this.setThumbBoxSize();
29927         }
29928         
29929         this.setThumbBoxPosition();
29930         
29931         this.baseScaleLevel();
29932         
29933         this.draw();
29934         
29935         this.resize();
29936         
29937         this.canvasLoaded = true;
29938         
29939         if(this.loadMask){
29940             this.maskEl.unmask();
29941         }
29942         
29943     },
29944     
29945     setCanvasPosition : function()
29946     {   
29947         if(!this.canvasEl){
29948             return;
29949         }
29950         
29951         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29952         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29953         
29954         this.previewEl.setLeft(pw);
29955         this.previewEl.setTop(ph);
29956         
29957     },
29958     
29959     onMouseDown : function(e)
29960     {   
29961         e.stopEvent();
29962         
29963         this.dragable = true;
29964         this.pinching = false;
29965         
29966         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29967             this.dragable = false;
29968             return;
29969         }
29970         
29971         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29972         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29973         
29974     },
29975     
29976     onMouseMove : function(e)
29977     {   
29978         e.stopEvent();
29979         
29980         if(!this.canvasLoaded){
29981             return;
29982         }
29983         
29984         if (!this.dragable){
29985             return;
29986         }
29987         
29988         var minX = Math.ceil(this.thumbEl.getLeft(true));
29989         var minY = Math.ceil(this.thumbEl.getTop(true));
29990         
29991         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29992         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29993         
29994         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29995         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29996         
29997         x = x - this.mouseX;
29998         y = y - this.mouseY;
29999         
30000         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30001         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30002         
30003         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30004         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30005         
30006         this.previewEl.setLeft(bgX);
30007         this.previewEl.setTop(bgY);
30008         
30009         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30010         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30011     },
30012     
30013     onMouseUp : function(e)
30014     {   
30015         e.stopEvent();
30016         
30017         this.dragable = false;
30018     },
30019     
30020     onMouseWheel : function(e)
30021     {   
30022         e.stopEvent();
30023         
30024         this.startScale = this.scale;
30025         
30026         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30027         
30028         if(!this.zoomable()){
30029             this.scale = this.startScale;
30030             return;
30031         }
30032         
30033         this.draw();
30034         
30035         return;
30036     },
30037     
30038     zoomable : function()
30039     {
30040         var minScale = this.thumbEl.getWidth() / this.minWidth;
30041         
30042         if(this.minWidth < this.minHeight){
30043             minScale = this.thumbEl.getHeight() / this.minHeight;
30044         }
30045         
30046         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30047         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30048         
30049         if(
30050                 this.isDocument &&
30051                 (this.rotate == 0 || this.rotate == 180) && 
30052                 (
30053                     width > this.imageEl.OriginWidth || 
30054                     height > this.imageEl.OriginHeight ||
30055                     (width < this.minWidth && height < this.minHeight)
30056                 )
30057         ){
30058             return false;
30059         }
30060         
30061         if(
30062                 this.isDocument &&
30063                 (this.rotate == 90 || this.rotate == 270) && 
30064                 (
30065                     width > this.imageEl.OriginWidth || 
30066                     height > this.imageEl.OriginHeight ||
30067                     (width < this.minHeight && height < this.minWidth)
30068                 )
30069         ){
30070             return false;
30071         }
30072         
30073         if(
30074                 !this.isDocument &&
30075                 (this.rotate == 0 || this.rotate == 180) && 
30076                 (
30077                     width < this.minWidth || 
30078                     width > this.imageEl.OriginWidth || 
30079                     height < this.minHeight || 
30080                     height > this.imageEl.OriginHeight
30081                 )
30082         ){
30083             return false;
30084         }
30085         
30086         if(
30087                 !this.isDocument &&
30088                 (this.rotate == 90 || this.rotate == 270) && 
30089                 (
30090                     width < this.minHeight || 
30091                     width > this.imageEl.OriginWidth || 
30092                     height < this.minWidth || 
30093                     height > this.imageEl.OriginHeight
30094                 )
30095         ){
30096             return false;
30097         }
30098         
30099         return true;
30100         
30101     },
30102     
30103     onRotateLeft : function(e)
30104     {   
30105         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30106             
30107             var minScale = this.thumbEl.getWidth() / this.minWidth;
30108             
30109             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30110             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30111             
30112             this.startScale = this.scale;
30113             
30114             while (this.getScaleLevel() < minScale){
30115             
30116                 this.scale = this.scale + 1;
30117                 
30118                 if(!this.zoomable()){
30119                     break;
30120                 }
30121                 
30122                 if(
30123                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30124                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30125                 ){
30126                     continue;
30127                 }
30128                 
30129                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30130
30131                 this.draw();
30132                 
30133                 return;
30134             }
30135             
30136             this.scale = this.startScale;
30137             
30138             this.onRotateFail();
30139             
30140             return false;
30141         }
30142         
30143         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30144
30145         if(this.isDocument){
30146             this.setThumbBoxSize();
30147             this.setThumbBoxPosition();
30148             this.setCanvasPosition();
30149         }
30150         
30151         this.draw();
30152         
30153         this.fireEvent('rotate', this, 'left');
30154         
30155     },
30156     
30157     onRotateRight : function(e)
30158     {
30159         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30160             
30161             var minScale = this.thumbEl.getWidth() / this.minWidth;
30162         
30163             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30164             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30165             
30166             this.startScale = this.scale;
30167             
30168             while (this.getScaleLevel() < minScale){
30169             
30170                 this.scale = this.scale + 1;
30171                 
30172                 if(!this.zoomable()){
30173                     break;
30174                 }
30175                 
30176                 if(
30177                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30178                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30179                 ){
30180                     continue;
30181                 }
30182                 
30183                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30184
30185                 this.draw();
30186                 
30187                 return;
30188             }
30189             
30190             this.scale = this.startScale;
30191             
30192             this.onRotateFail();
30193             
30194             return false;
30195         }
30196         
30197         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30198
30199         if(this.isDocument){
30200             this.setThumbBoxSize();
30201             this.setThumbBoxPosition();
30202             this.setCanvasPosition();
30203         }
30204         
30205         this.draw();
30206         
30207         this.fireEvent('rotate', this, 'right');
30208     },
30209     
30210     onRotateFail : function()
30211     {
30212         this.errorEl.show(true);
30213         
30214         var _this = this;
30215         
30216         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30217     },
30218     
30219     draw : function()
30220     {
30221         this.previewEl.dom.innerHTML = '';
30222         
30223         var canvasEl = document.createElement("canvas");
30224         
30225         var contextEl = canvasEl.getContext("2d");
30226         
30227         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30228         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30229         var center = this.imageEl.OriginWidth / 2;
30230         
30231         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30232             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30233             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30234             center = this.imageEl.OriginHeight / 2;
30235         }
30236         
30237         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30238         
30239         contextEl.translate(center, center);
30240         contextEl.rotate(this.rotate * Math.PI / 180);
30241
30242         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30243         
30244         this.canvasEl = document.createElement("canvas");
30245         
30246         this.contextEl = this.canvasEl.getContext("2d");
30247         
30248         switch (this.rotate) {
30249             case 0 :
30250                 
30251                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30252                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30253                 
30254                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30255                 
30256                 break;
30257             case 90 : 
30258                 
30259                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30260                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30261                 
30262                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30263                     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);
30264                     break;
30265                 }
30266                 
30267                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30268                 
30269                 break;
30270             case 180 :
30271                 
30272                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30273                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30274                 
30275                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30276                     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);
30277                     break;
30278                 }
30279                 
30280                 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);
30281                 
30282                 break;
30283             case 270 :
30284                 
30285                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30286                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30287         
30288                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30289                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30290                     break;
30291                 }
30292                 
30293                 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);
30294                 
30295                 break;
30296             default : 
30297                 break;
30298         }
30299         
30300         this.previewEl.appendChild(this.canvasEl);
30301         
30302         this.setCanvasPosition();
30303     },
30304     
30305     crop : function()
30306     {
30307         if(!this.canvasLoaded){
30308             return;
30309         }
30310         
30311         var imageCanvas = document.createElement("canvas");
30312         
30313         var imageContext = imageCanvas.getContext("2d");
30314         
30315         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30316         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30317         
30318         var center = imageCanvas.width / 2;
30319         
30320         imageContext.translate(center, center);
30321         
30322         imageContext.rotate(this.rotate * Math.PI / 180);
30323         
30324         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30325         
30326         var canvas = document.createElement("canvas");
30327         
30328         var context = canvas.getContext("2d");
30329                 
30330         canvas.width = this.minWidth;
30331         canvas.height = this.minHeight;
30332
30333         switch (this.rotate) {
30334             case 0 :
30335                 
30336                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30337                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30338                 
30339                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30340                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30341                 
30342                 var targetWidth = this.minWidth - 2 * x;
30343                 var targetHeight = this.minHeight - 2 * y;
30344                 
30345                 var scale = 1;
30346                 
30347                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30348                     scale = targetWidth / width;
30349                 }
30350                 
30351                 if(x > 0 && y == 0){
30352                     scale = targetHeight / height;
30353                 }
30354                 
30355                 if(x > 0 && y > 0){
30356                     scale = targetWidth / width;
30357                     
30358                     if(width < height){
30359                         scale = targetHeight / height;
30360                     }
30361                 }
30362                 
30363                 context.scale(scale, scale);
30364                 
30365                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30366                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30367
30368                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30369                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30370
30371                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30372                 
30373                 break;
30374             case 90 : 
30375                 
30376                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30377                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30378                 
30379                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30380                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30381                 
30382                 var targetWidth = this.minWidth - 2 * x;
30383                 var targetHeight = this.minHeight - 2 * y;
30384                 
30385                 var scale = 1;
30386                 
30387                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30388                     scale = targetWidth / width;
30389                 }
30390                 
30391                 if(x > 0 && y == 0){
30392                     scale = targetHeight / height;
30393                 }
30394                 
30395                 if(x > 0 && y > 0){
30396                     scale = targetWidth / width;
30397                     
30398                     if(width < height){
30399                         scale = targetHeight / height;
30400                     }
30401                 }
30402                 
30403                 context.scale(scale, scale);
30404                 
30405                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30406                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30407
30408                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30409                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30410                 
30411                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30412                 
30413                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30414                 
30415                 break;
30416             case 180 :
30417                 
30418                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30419                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30420                 
30421                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30422                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30423                 
30424                 var targetWidth = this.minWidth - 2 * x;
30425                 var targetHeight = this.minHeight - 2 * y;
30426                 
30427                 var scale = 1;
30428                 
30429                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30430                     scale = targetWidth / width;
30431                 }
30432                 
30433                 if(x > 0 && y == 0){
30434                     scale = targetHeight / height;
30435                 }
30436                 
30437                 if(x > 0 && y > 0){
30438                     scale = targetWidth / width;
30439                     
30440                     if(width < height){
30441                         scale = targetHeight / height;
30442                     }
30443                 }
30444                 
30445                 context.scale(scale, scale);
30446                 
30447                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30448                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30449
30450                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30451                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30452
30453                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30454                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30455                 
30456                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30457                 
30458                 break;
30459             case 270 :
30460                 
30461                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30462                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30463                 
30464                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30465                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30466                 
30467                 var targetWidth = this.minWidth - 2 * x;
30468                 var targetHeight = this.minHeight - 2 * y;
30469                 
30470                 var scale = 1;
30471                 
30472                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30473                     scale = targetWidth / width;
30474                 }
30475                 
30476                 if(x > 0 && y == 0){
30477                     scale = targetHeight / height;
30478                 }
30479                 
30480                 if(x > 0 && y > 0){
30481                     scale = targetWidth / width;
30482                     
30483                     if(width < height){
30484                         scale = targetHeight / height;
30485                     }
30486                 }
30487                 
30488                 context.scale(scale, scale);
30489                 
30490                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30491                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30492
30493                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30494                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30495                 
30496                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30497                 
30498                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30499                 
30500                 break;
30501             default : 
30502                 break;
30503         }
30504         
30505         this.cropData = canvas.toDataURL(this.cropType);
30506         
30507         if(this.fireEvent('crop', this, this.cropData) !== false){
30508             this.process(this.file, this.cropData);
30509         }
30510         
30511         return;
30512         
30513     },
30514     
30515     setThumbBoxSize : function()
30516     {
30517         var width, height;
30518         
30519         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30520             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30521             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30522             
30523             this.minWidth = width;
30524             this.minHeight = height;
30525             
30526             if(this.rotate == 90 || this.rotate == 270){
30527                 this.minWidth = height;
30528                 this.minHeight = width;
30529             }
30530         }
30531         
30532         height = 300;
30533         width = Math.ceil(this.minWidth * height / this.minHeight);
30534         
30535         if(this.minWidth > this.minHeight){
30536             width = 300;
30537             height = Math.ceil(this.minHeight * width / this.minWidth);
30538         }
30539         
30540         this.thumbEl.setStyle({
30541             width : width + 'px',
30542             height : height + 'px'
30543         });
30544
30545         return;
30546             
30547     },
30548     
30549     setThumbBoxPosition : function()
30550     {
30551         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30552         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30553         
30554         this.thumbEl.setLeft(x);
30555         this.thumbEl.setTop(y);
30556         
30557     },
30558     
30559     baseRotateLevel : function()
30560     {
30561         this.baseRotate = 1;
30562         
30563         if(
30564                 typeof(this.exif) != 'undefined' &&
30565                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30566                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30567         ){
30568             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30569         }
30570         
30571         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30572         
30573     },
30574     
30575     baseScaleLevel : function()
30576     {
30577         var width, height;
30578         
30579         if(this.isDocument){
30580             
30581             if(this.baseRotate == 6 || this.baseRotate == 8){
30582             
30583                 height = this.thumbEl.getHeight();
30584                 this.baseScale = height / this.imageEl.OriginWidth;
30585
30586                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30587                     width = this.thumbEl.getWidth();
30588                     this.baseScale = width / this.imageEl.OriginHeight;
30589                 }
30590
30591                 return;
30592             }
30593
30594             height = this.thumbEl.getHeight();
30595             this.baseScale = height / this.imageEl.OriginHeight;
30596
30597             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30598                 width = this.thumbEl.getWidth();
30599                 this.baseScale = width / this.imageEl.OriginWidth;
30600             }
30601
30602             return;
30603         }
30604         
30605         if(this.baseRotate == 6 || this.baseRotate == 8){
30606             
30607             width = this.thumbEl.getHeight();
30608             this.baseScale = width / this.imageEl.OriginHeight;
30609             
30610             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30611                 height = this.thumbEl.getWidth();
30612                 this.baseScale = height / this.imageEl.OriginHeight;
30613             }
30614             
30615             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30616                 height = this.thumbEl.getWidth();
30617                 this.baseScale = height / this.imageEl.OriginHeight;
30618                 
30619                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30620                     width = this.thumbEl.getHeight();
30621                     this.baseScale = width / this.imageEl.OriginWidth;
30622                 }
30623             }
30624             
30625             return;
30626         }
30627         
30628         width = this.thumbEl.getWidth();
30629         this.baseScale = width / this.imageEl.OriginWidth;
30630         
30631         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30632             height = this.thumbEl.getHeight();
30633             this.baseScale = height / this.imageEl.OriginHeight;
30634         }
30635         
30636         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30637             
30638             height = this.thumbEl.getHeight();
30639             this.baseScale = height / this.imageEl.OriginHeight;
30640             
30641             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30642                 width = this.thumbEl.getWidth();
30643                 this.baseScale = width / this.imageEl.OriginWidth;
30644             }
30645             
30646         }
30647         
30648         return;
30649     },
30650     
30651     getScaleLevel : function()
30652     {
30653         return this.baseScale * Math.pow(1.1, this.scale);
30654     },
30655     
30656     onTouchStart : function(e)
30657     {
30658         if(!this.canvasLoaded){
30659             this.beforeSelectFile(e);
30660             return;
30661         }
30662         
30663         var touches = e.browserEvent.touches;
30664         
30665         if(!touches){
30666             return;
30667         }
30668         
30669         if(touches.length == 1){
30670             this.onMouseDown(e);
30671             return;
30672         }
30673         
30674         if(touches.length != 2){
30675             return;
30676         }
30677         
30678         var coords = [];
30679         
30680         for(var i = 0, finger; finger = touches[i]; i++){
30681             coords.push(finger.pageX, finger.pageY);
30682         }
30683         
30684         var x = Math.pow(coords[0] - coords[2], 2);
30685         var y = Math.pow(coords[1] - coords[3], 2);
30686         
30687         this.startDistance = Math.sqrt(x + y);
30688         
30689         this.startScale = this.scale;
30690         
30691         this.pinching = true;
30692         this.dragable = false;
30693         
30694     },
30695     
30696     onTouchMove : function(e)
30697     {
30698         if(!this.pinching && !this.dragable){
30699             return;
30700         }
30701         
30702         var touches = e.browserEvent.touches;
30703         
30704         if(!touches){
30705             return;
30706         }
30707         
30708         if(this.dragable){
30709             this.onMouseMove(e);
30710             return;
30711         }
30712         
30713         var coords = [];
30714         
30715         for(var i = 0, finger; finger = touches[i]; i++){
30716             coords.push(finger.pageX, finger.pageY);
30717         }
30718         
30719         var x = Math.pow(coords[0] - coords[2], 2);
30720         var y = Math.pow(coords[1] - coords[3], 2);
30721         
30722         this.endDistance = Math.sqrt(x + y);
30723         
30724         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30725         
30726         if(!this.zoomable()){
30727             this.scale = this.startScale;
30728             return;
30729         }
30730         
30731         this.draw();
30732         
30733     },
30734     
30735     onTouchEnd : function(e)
30736     {
30737         this.pinching = false;
30738         this.dragable = false;
30739         
30740     },
30741     
30742     process : function(file, crop)
30743     {
30744         if(this.loadMask){
30745             this.maskEl.mask(this.loadingText);
30746         }
30747         
30748         this.xhr = new XMLHttpRequest();
30749         
30750         file.xhr = this.xhr;
30751
30752         this.xhr.open(this.method, this.url, true);
30753         
30754         var headers = {
30755             "Accept": "application/json",
30756             "Cache-Control": "no-cache",
30757             "X-Requested-With": "XMLHttpRequest"
30758         };
30759         
30760         for (var headerName in headers) {
30761             var headerValue = headers[headerName];
30762             if (headerValue) {
30763                 this.xhr.setRequestHeader(headerName, headerValue);
30764             }
30765         }
30766         
30767         var _this = this;
30768         
30769         this.xhr.onload = function()
30770         {
30771             _this.xhrOnLoad(_this.xhr);
30772         }
30773         
30774         this.xhr.onerror = function()
30775         {
30776             _this.xhrOnError(_this.xhr);
30777         }
30778         
30779         var formData = new FormData();
30780
30781         formData.append('returnHTML', 'NO');
30782         
30783         if(crop){
30784             formData.append('crop', crop);
30785         }
30786         
30787         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30788             formData.append(this.paramName, file, file.name);
30789         }
30790         
30791         if(typeof(file.filename) != 'undefined'){
30792             formData.append('filename', file.filename);
30793         }
30794         
30795         if(typeof(file.mimetype) != 'undefined'){
30796             formData.append('mimetype', file.mimetype);
30797         }
30798         
30799         if(this.fireEvent('arrange', this, formData) != false){
30800             this.xhr.send(formData);
30801         };
30802     },
30803     
30804     xhrOnLoad : function(xhr)
30805     {
30806         if(this.loadMask){
30807             this.maskEl.unmask();
30808         }
30809         
30810         if (xhr.readyState !== 4) {
30811             this.fireEvent('exception', this, xhr);
30812             return;
30813         }
30814
30815         var response = Roo.decode(xhr.responseText);
30816         
30817         if(!response.success){
30818             this.fireEvent('exception', this, xhr);
30819             return;
30820         }
30821         
30822         var response = Roo.decode(xhr.responseText);
30823         
30824         this.fireEvent('upload', this, response);
30825         
30826     },
30827     
30828     xhrOnError : function()
30829     {
30830         if(this.loadMask){
30831             this.maskEl.unmask();
30832         }
30833         
30834         Roo.log('xhr on error');
30835         
30836         var response = Roo.decode(xhr.responseText);
30837           
30838         Roo.log(response);
30839         
30840     },
30841     
30842     prepare : function(file)
30843     {   
30844         if(this.loadMask){
30845             this.maskEl.mask(this.loadingText);
30846         }
30847         
30848         this.file = false;
30849         this.exif = {};
30850         
30851         if(typeof(file) === 'string'){
30852             this.loadCanvas(file);
30853             return;
30854         }
30855         
30856         if(!file || !this.urlAPI){
30857             return;
30858         }
30859         
30860         this.file = file;
30861         this.cropType = file.type;
30862         
30863         var _this = this;
30864         
30865         if(this.fireEvent('prepare', this, this.file) != false){
30866             
30867             var reader = new FileReader();
30868             
30869             reader.onload = function (e) {
30870                 if (e.target.error) {
30871                     Roo.log(e.target.error);
30872                     return;
30873                 }
30874                 
30875                 var buffer = e.target.result,
30876                     dataView = new DataView(buffer),
30877                     offset = 2,
30878                     maxOffset = dataView.byteLength - 4,
30879                     markerBytes,
30880                     markerLength;
30881                 
30882                 if (dataView.getUint16(0) === 0xffd8) {
30883                     while (offset < maxOffset) {
30884                         markerBytes = dataView.getUint16(offset);
30885                         
30886                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30887                             markerLength = dataView.getUint16(offset + 2) + 2;
30888                             if (offset + markerLength > dataView.byteLength) {
30889                                 Roo.log('Invalid meta data: Invalid segment size.');
30890                                 break;
30891                             }
30892                             
30893                             if(markerBytes == 0xffe1){
30894                                 _this.parseExifData(
30895                                     dataView,
30896                                     offset,
30897                                     markerLength
30898                                 );
30899                             }
30900                             
30901                             offset += markerLength;
30902                             
30903                             continue;
30904                         }
30905                         
30906                         break;
30907                     }
30908                     
30909                 }
30910                 
30911                 var url = _this.urlAPI.createObjectURL(_this.file);
30912                 
30913                 _this.loadCanvas(url);
30914                 
30915                 return;
30916             }
30917             
30918             reader.readAsArrayBuffer(this.file);
30919             
30920         }
30921         
30922     },
30923     
30924     parseExifData : function(dataView, offset, length)
30925     {
30926         var tiffOffset = offset + 10,
30927             littleEndian,
30928             dirOffset;
30929     
30930         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30931             // No Exif data, might be XMP data instead
30932             return;
30933         }
30934         
30935         // Check for the ASCII code for "Exif" (0x45786966):
30936         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30937             // No Exif data, might be XMP data instead
30938             return;
30939         }
30940         if (tiffOffset + 8 > dataView.byteLength) {
30941             Roo.log('Invalid Exif data: Invalid segment size.');
30942             return;
30943         }
30944         // Check for the two null bytes:
30945         if (dataView.getUint16(offset + 8) !== 0x0000) {
30946             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30947             return;
30948         }
30949         // Check the byte alignment:
30950         switch (dataView.getUint16(tiffOffset)) {
30951         case 0x4949:
30952             littleEndian = true;
30953             break;
30954         case 0x4D4D:
30955             littleEndian = false;
30956             break;
30957         default:
30958             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30959             return;
30960         }
30961         // Check for the TIFF tag marker (0x002A):
30962         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30963             Roo.log('Invalid Exif data: Missing TIFF marker.');
30964             return;
30965         }
30966         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30967         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30968         
30969         this.parseExifTags(
30970             dataView,
30971             tiffOffset,
30972             tiffOffset + dirOffset,
30973             littleEndian
30974         );
30975     },
30976     
30977     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30978     {
30979         var tagsNumber,
30980             dirEndOffset,
30981             i;
30982         if (dirOffset + 6 > dataView.byteLength) {
30983             Roo.log('Invalid Exif data: Invalid directory offset.');
30984             return;
30985         }
30986         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30987         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30988         if (dirEndOffset + 4 > dataView.byteLength) {
30989             Roo.log('Invalid Exif data: Invalid directory size.');
30990             return;
30991         }
30992         for (i = 0; i < tagsNumber; i += 1) {
30993             this.parseExifTag(
30994                 dataView,
30995                 tiffOffset,
30996                 dirOffset + 2 + 12 * i, // tag offset
30997                 littleEndian
30998             );
30999         }
31000         // Return the offset to the next directory:
31001         return dataView.getUint32(dirEndOffset, littleEndian);
31002     },
31003     
31004     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31005     {
31006         var tag = dataView.getUint16(offset, littleEndian);
31007         
31008         this.exif[tag] = this.getExifValue(
31009             dataView,
31010             tiffOffset,
31011             offset,
31012             dataView.getUint16(offset + 2, littleEndian), // tag type
31013             dataView.getUint32(offset + 4, littleEndian), // tag length
31014             littleEndian
31015         );
31016     },
31017     
31018     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31019     {
31020         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31021             tagSize,
31022             dataOffset,
31023             values,
31024             i,
31025             str,
31026             c;
31027     
31028         if (!tagType) {
31029             Roo.log('Invalid Exif data: Invalid tag type.');
31030             return;
31031         }
31032         
31033         tagSize = tagType.size * length;
31034         // Determine if the value is contained in the dataOffset bytes,
31035         // or if the value at the dataOffset is a pointer to the actual data:
31036         dataOffset = tagSize > 4 ?
31037                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31038         if (dataOffset + tagSize > dataView.byteLength) {
31039             Roo.log('Invalid Exif data: Invalid data offset.');
31040             return;
31041         }
31042         if (length === 1) {
31043             return tagType.getValue(dataView, dataOffset, littleEndian);
31044         }
31045         values = [];
31046         for (i = 0; i < length; i += 1) {
31047             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31048         }
31049         
31050         if (tagType.ascii) {
31051             str = '';
31052             // Concatenate the chars:
31053             for (i = 0; i < values.length; i += 1) {
31054                 c = values[i];
31055                 // Ignore the terminating NULL byte(s):
31056                 if (c === '\u0000') {
31057                     break;
31058                 }
31059                 str += c;
31060             }
31061             return str;
31062         }
31063         return values;
31064     }
31065     
31066 });
31067
31068 Roo.apply(Roo.bootstrap.UploadCropbox, {
31069     tags : {
31070         'Orientation': 0x0112
31071     },
31072     
31073     Orientation: {
31074             1: 0, //'top-left',
31075 //            2: 'top-right',
31076             3: 180, //'bottom-right',
31077 //            4: 'bottom-left',
31078 //            5: 'left-top',
31079             6: 90, //'right-top',
31080 //            7: 'right-bottom',
31081             8: 270 //'left-bottom'
31082     },
31083     
31084     exifTagTypes : {
31085         // byte, 8-bit unsigned int:
31086         1: {
31087             getValue: function (dataView, dataOffset) {
31088                 return dataView.getUint8(dataOffset);
31089             },
31090             size: 1
31091         },
31092         // ascii, 8-bit byte:
31093         2: {
31094             getValue: function (dataView, dataOffset) {
31095                 return String.fromCharCode(dataView.getUint8(dataOffset));
31096             },
31097             size: 1,
31098             ascii: true
31099         },
31100         // short, 16 bit int:
31101         3: {
31102             getValue: function (dataView, dataOffset, littleEndian) {
31103                 return dataView.getUint16(dataOffset, littleEndian);
31104             },
31105             size: 2
31106         },
31107         // long, 32 bit int:
31108         4: {
31109             getValue: function (dataView, dataOffset, littleEndian) {
31110                 return dataView.getUint32(dataOffset, littleEndian);
31111             },
31112             size: 4
31113         },
31114         // rational = two long values, first is numerator, second is denominator:
31115         5: {
31116             getValue: function (dataView, dataOffset, littleEndian) {
31117                 return dataView.getUint32(dataOffset, littleEndian) /
31118                     dataView.getUint32(dataOffset + 4, littleEndian);
31119             },
31120             size: 8
31121         },
31122         // slong, 32 bit signed int:
31123         9: {
31124             getValue: function (dataView, dataOffset, littleEndian) {
31125                 return dataView.getInt32(dataOffset, littleEndian);
31126             },
31127             size: 4
31128         },
31129         // srational, two slongs, first is numerator, second is denominator:
31130         10: {
31131             getValue: function (dataView, dataOffset, littleEndian) {
31132                 return dataView.getInt32(dataOffset, littleEndian) /
31133                     dataView.getInt32(dataOffset + 4, littleEndian);
31134             },
31135             size: 8
31136         }
31137     },
31138     
31139     footer : {
31140         STANDARD : [
31141             {
31142                 tag : 'div',
31143                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31144                 action : 'rotate-left',
31145                 cn : [
31146                     {
31147                         tag : 'button',
31148                         cls : 'btn btn-default',
31149                         html : '<i class="fa fa-undo"></i>'
31150                     }
31151                 ]
31152             },
31153             {
31154                 tag : 'div',
31155                 cls : 'btn-group roo-upload-cropbox-picture',
31156                 action : 'picture',
31157                 cn : [
31158                     {
31159                         tag : 'button',
31160                         cls : 'btn btn-default',
31161                         html : '<i class="fa fa-picture-o"></i>'
31162                     }
31163                 ]
31164             },
31165             {
31166                 tag : 'div',
31167                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31168                 action : 'rotate-right',
31169                 cn : [
31170                     {
31171                         tag : 'button',
31172                         cls : 'btn btn-default',
31173                         html : '<i class="fa fa-repeat"></i>'
31174                     }
31175                 ]
31176             }
31177         ],
31178         DOCUMENT : [
31179             {
31180                 tag : 'div',
31181                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31182                 action : 'rotate-left',
31183                 cn : [
31184                     {
31185                         tag : 'button',
31186                         cls : 'btn btn-default',
31187                         html : '<i class="fa fa-undo"></i>'
31188                     }
31189                 ]
31190             },
31191             {
31192                 tag : 'div',
31193                 cls : 'btn-group roo-upload-cropbox-download',
31194                 action : 'download',
31195                 cn : [
31196                     {
31197                         tag : 'button',
31198                         cls : 'btn btn-default',
31199                         html : '<i class="fa fa-download"></i>'
31200                     }
31201                 ]
31202             },
31203             {
31204                 tag : 'div',
31205                 cls : 'btn-group roo-upload-cropbox-crop',
31206                 action : 'crop',
31207                 cn : [
31208                     {
31209                         tag : 'button',
31210                         cls : 'btn btn-default',
31211                         html : '<i class="fa fa-crop"></i>'
31212                     }
31213                 ]
31214             },
31215             {
31216                 tag : 'div',
31217                 cls : 'btn-group roo-upload-cropbox-trash',
31218                 action : 'trash',
31219                 cn : [
31220                     {
31221                         tag : 'button',
31222                         cls : 'btn btn-default',
31223                         html : '<i class="fa fa-trash"></i>'
31224                     }
31225                 ]
31226             },
31227             {
31228                 tag : 'div',
31229                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31230                 action : 'rotate-right',
31231                 cn : [
31232                     {
31233                         tag : 'button',
31234                         cls : 'btn btn-default',
31235                         html : '<i class="fa fa-repeat"></i>'
31236                     }
31237                 ]
31238             }
31239         ],
31240         ROTATOR : [
31241             {
31242                 tag : 'div',
31243                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31244                 action : 'rotate-left',
31245                 cn : [
31246                     {
31247                         tag : 'button',
31248                         cls : 'btn btn-default',
31249                         html : '<i class="fa fa-undo"></i>'
31250                     }
31251                 ]
31252             },
31253             {
31254                 tag : 'div',
31255                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31256                 action : 'rotate-right',
31257                 cn : [
31258                     {
31259                         tag : 'button',
31260                         cls : 'btn btn-default',
31261                         html : '<i class="fa fa-repeat"></i>'
31262                     }
31263                 ]
31264             }
31265         ]
31266     }
31267 });
31268
31269 /*
31270 * Licence: LGPL
31271 */
31272
31273 /**
31274  * @class Roo.bootstrap.DocumentManager
31275  * @extends Roo.bootstrap.Component
31276  * Bootstrap DocumentManager class
31277  * @cfg {String} paramName default 'imageUpload'
31278  * @cfg {String} toolTipName default 'filename'
31279  * @cfg {String} method default POST
31280  * @cfg {String} url action url
31281  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31282  * @cfg {Boolean} multiple multiple upload default true
31283  * @cfg {Number} thumbSize default 300
31284  * @cfg {String} fieldLabel
31285  * @cfg {Number} labelWidth default 4
31286  * @cfg {String} labelAlign (left|top) default left
31287  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31288 * @cfg {Number} labellg set the width of label (1-12)
31289  * @cfg {Number} labelmd set the width of label (1-12)
31290  * @cfg {Number} labelsm set the width of label (1-12)
31291  * @cfg {Number} labelxs set the width of label (1-12)
31292  * 
31293  * @constructor
31294  * Create a new DocumentManager
31295  * @param {Object} config The config object
31296  */
31297
31298 Roo.bootstrap.DocumentManager = function(config){
31299     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31300     
31301     this.files = [];
31302     this.delegates = [];
31303     
31304     this.addEvents({
31305         /**
31306          * @event initial
31307          * Fire when initial the DocumentManager
31308          * @param {Roo.bootstrap.DocumentManager} this
31309          */
31310         "initial" : true,
31311         /**
31312          * @event inspect
31313          * inspect selected file
31314          * @param {Roo.bootstrap.DocumentManager} this
31315          * @param {File} file
31316          */
31317         "inspect" : true,
31318         /**
31319          * @event exception
31320          * Fire when xhr load exception
31321          * @param {Roo.bootstrap.DocumentManager} this
31322          * @param {XMLHttpRequest} xhr
31323          */
31324         "exception" : true,
31325         /**
31326          * @event afterupload
31327          * Fire when xhr load exception
31328          * @param {Roo.bootstrap.DocumentManager} this
31329          * @param {XMLHttpRequest} xhr
31330          */
31331         "afterupload" : true,
31332         /**
31333          * @event prepare
31334          * prepare the form data
31335          * @param {Roo.bootstrap.DocumentManager} this
31336          * @param {Object} formData
31337          */
31338         "prepare" : true,
31339         /**
31340          * @event remove
31341          * Fire when remove the file
31342          * @param {Roo.bootstrap.DocumentManager} this
31343          * @param {Object} file
31344          */
31345         "remove" : true,
31346         /**
31347          * @event refresh
31348          * Fire after refresh the file
31349          * @param {Roo.bootstrap.DocumentManager} this
31350          */
31351         "refresh" : true,
31352         /**
31353          * @event click
31354          * Fire after click the image
31355          * @param {Roo.bootstrap.DocumentManager} this
31356          * @param {Object} file
31357          */
31358         "click" : true,
31359         /**
31360          * @event edit
31361          * Fire when upload a image and editable set to true
31362          * @param {Roo.bootstrap.DocumentManager} this
31363          * @param {Object} file
31364          */
31365         "edit" : true,
31366         /**
31367          * @event beforeselectfile
31368          * Fire before select file
31369          * @param {Roo.bootstrap.DocumentManager} this
31370          */
31371         "beforeselectfile" : true,
31372         /**
31373          * @event process
31374          * Fire before process file
31375          * @param {Roo.bootstrap.DocumentManager} this
31376          * @param {Object} file
31377          */
31378         "process" : true,
31379         /**
31380          * @event previewrendered
31381          * Fire when preview rendered
31382          * @param {Roo.bootstrap.DocumentManager} this
31383          * @param {Object} file
31384          */
31385         "previewrendered" : true,
31386         /**
31387          */
31388         "previewResize" : true
31389         
31390     });
31391 };
31392
31393 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31394     
31395     boxes : 0,
31396     inputName : '',
31397     thumbSize : 300,
31398     multiple : true,
31399     files : false,
31400     method : 'POST',
31401     url : '',
31402     paramName : 'imageUpload',
31403     toolTipName : 'filename',
31404     fieldLabel : '',
31405     labelWidth : 4,
31406     labelAlign : 'left',
31407     editable : true,
31408     delegates : false,
31409     xhr : false, 
31410     
31411     labellg : 0,
31412     labelmd : 0,
31413     labelsm : 0,
31414     labelxs : 0,
31415     
31416     getAutoCreate : function()
31417     {   
31418         var managerWidget = {
31419             tag : 'div',
31420             cls : 'roo-document-manager',
31421             cn : [
31422                 {
31423                     tag : 'input',
31424                     cls : 'roo-document-manager-selector',
31425                     type : 'file'
31426                 },
31427                 {
31428                     tag : 'div',
31429                     cls : 'roo-document-manager-uploader',
31430                     cn : [
31431                         {
31432                             tag : 'div',
31433                             cls : 'roo-document-manager-upload-btn',
31434                             html : '<i class="fa fa-plus"></i>'
31435                         }
31436                     ]
31437                     
31438                 }
31439             ]
31440         };
31441         
31442         var content = [
31443             {
31444                 tag : 'div',
31445                 cls : 'column col-md-12',
31446                 cn : managerWidget
31447             }
31448         ];
31449         
31450         if(this.fieldLabel.length){
31451             
31452             content = [
31453                 {
31454                     tag : 'div',
31455                     cls : 'column col-md-12',
31456                     html : this.fieldLabel
31457                 },
31458                 {
31459                     tag : 'div',
31460                     cls : 'column col-md-12',
31461                     cn : managerWidget
31462                 }
31463             ];
31464
31465             if(this.labelAlign == 'left'){
31466                 content = [
31467                     {
31468                         tag : 'div',
31469                         cls : 'column',
31470                         html : this.fieldLabel
31471                     },
31472                     {
31473                         tag : 'div',
31474                         cls : 'column',
31475                         cn : managerWidget
31476                     }
31477                 ];
31478                 
31479                 if(this.labelWidth > 12){
31480                     content[0].style = "width: " + this.labelWidth + 'px';
31481                 }
31482
31483                 if(this.labelWidth < 13 && this.labelmd == 0){
31484                     this.labelmd = this.labelWidth;
31485                 }
31486
31487                 if(this.labellg > 0){
31488                     content[0].cls += ' col-lg-' + this.labellg;
31489                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31490                 }
31491
31492                 if(this.labelmd > 0){
31493                     content[0].cls += ' col-md-' + this.labelmd;
31494                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31495                 }
31496
31497                 if(this.labelsm > 0){
31498                     content[0].cls += ' col-sm-' + this.labelsm;
31499                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31500                 }
31501
31502                 if(this.labelxs > 0){
31503                     content[0].cls += ' col-xs-' + this.labelxs;
31504                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31505                 }
31506                 
31507             }
31508         }
31509         
31510         var cfg = {
31511             tag : 'div',
31512             cls : 'row clearfix',
31513             cn : content
31514         };
31515         
31516         return cfg;
31517         
31518     },
31519     
31520     initEvents : function()
31521     {
31522         this.managerEl = this.el.select('.roo-document-manager', true).first();
31523         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31524         
31525         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31526         this.selectorEl.hide();
31527         
31528         if(this.multiple){
31529             this.selectorEl.attr('multiple', 'multiple');
31530         }
31531         
31532         this.selectorEl.on('change', this.onFileSelected, this);
31533         
31534         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31535         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31536         
31537         this.uploader.on('click', this.onUploaderClick, this);
31538         
31539         this.renderProgressDialog();
31540         
31541         var _this = this;
31542         
31543         window.addEventListener("resize", function() { _this.refresh(); } );
31544         
31545         this.fireEvent('initial', this);
31546     },
31547     
31548     renderProgressDialog : function()
31549     {
31550         var _this = this;
31551         
31552         this.progressDialog = new Roo.bootstrap.Modal({
31553             cls : 'roo-document-manager-progress-dialog',
31554             allow_close : false,
31555             animate : false,
31556             title : '',
31557             buttons : [
31558                 {
31559                     name  :'cancel',
31560                     weight : 'danger',
31561                     html : 'Cancel'
31562                 }
31563             ], 
31564             listeners : { 
31565                 btnclick : function() {
31566                     _this.uploadCancel();
31567                     this.hide();
31568                 }
31569             }
31570         });
31571          
31572         this.progressDialog.render(Roo.get(document.body));
31573          
31574         this.progress = new Roo.bootstrap.Progress({
31575             cls : 'roo-document-manager-progress',
31576             active : true,
31577             striped : true
31578         });
31579         
31580         this.progress.render(this.progressDialog.getChildContainer());
31581         
31582         this.progressBar = new Roo.bootstrap.ProgressBar({
31583             cls : 'roo-document-manager-progress-bar',
31584             aria_valuenow : 0,
31585             aria_valuemin : 0,
31586             aria_valuemax : 12,
31587             panel : 'success'
31588         });
31589         
31590         this.progressBar.render(this.progress.getChildContainer());
31591     },
31592     
31593     onUploaderClick : function(e)
31594     {
31595         e.preventDefault();
31596      
31597         if(this.fireEvent('beforeselectfile', this) != false){
31598             this.selectorEl.dom.click();
31599         }
31600         
31601     },
31602     
31603     onFileSelected : function(e)
31604     {
31605         e.preventDefault();
31606         
31607         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31608             return;
31609         }
31610         
31611         Roo.each(this.selectorEl.dom.files, function(file){
31612             if(this.fireEvent('inspect', this, file) != false){
31613                 this.files.push(file);
31614             }
31615         }, this);
31616         
31617         this.queue();
31618         
31619     },
31620     
31621     queue : function()
31622     {
31623         this.selectorEl.dom.value = '';
31624         
31625         if(!this.files || !this.files.length){
31626             return;
31627         }
31628         
31629         if(this.boxes > 0 && this.files.length > this.boxes){
31630             this.files = this.files.slice(0, this.boxes);
31631         }
31632         
31633         this.uploader.show();
31634         
31635         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31636             this.uploader.hide();
31637         }
31638         
31639         var _this = this;
31640         
31641         var files = [];
31642         
31643         var docs = [];
31644         
31645         Roo.each(this.files, function(file){
31646             
31647             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31648                 var f = this.renderPreview(file);
31649                 files.push(f);
31650                 return;
31651             }
31652             
31653             if(file.type.indexOf('image') != -1){
31654                 this.delegates.push(
31655                     (function(){
31656                         _this.process(file);
31657                     }).createDelegate(this)
31658                 );
31659         
31660                 return;
31661             }
31662             
31663             docs.push(
31664                 (function(){
31665                     _this.process(file);
31666                 }).createDelegate(this)
31667             );
31668             
31669         }, this);
31670         
31671         this.files = files;
31672         
31673         this.delegates = this.delegates.concat(docs);
31674         
31675         if(!this.delegates.length){
31676             this.refresh();
31677             return;
31678         }
31679         
31680         this.progressBar.aria_valuemax = this.delegates.length;
31681         
31682         this.arrange();
31683         
31684         return;
31685     },
31686     
31687     arrange : function()
31688     {
31689         if(!this.delegates.length){
31690             this.progressDialog.hide();
31691             this.refresh();
31692             return;
31693         }
31694         
31695         var delegate = this.delegates.shift();
31696         
31697         this.progressDialog.show();
31698         
31699         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31700         
31701         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31702         
31703         delegate();
31704     },
31705     
31706     refresh : function()
31707     {
31708         this.uploader.show();
31709         
31710         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31711             this.uploader.hide();
31712         }
31713         
31714         Roo.isTouch ? this.closable(false) : this.closable(true);
31715         
31716         this.fireEvent('refresh', this);
31717     },
31718     
31719     onRemove : function(e, el, o)
31720     {
31721         e.preventDefault();
31722         
31723         this.fireEvent('remove', this, o);
31724         
31725     },
31726     
31727     remove : function(o)
31728     {
31729         var files = [];
31730         
31731         Roo.each(this.files, function(file){
31732             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31733                 files.push(file);
31734                 return;
31735             }
31736
31737             o.target.remove();
31738
31739         }, this);
31740         
31741         this.files = files;
31742         
31743         this.refresh();
31744     },
31745     
31746     clear : function()
31747     {
31748         Roo.each(this.files, function(file){
31749             if(!file.target){
31750                 return;
31751             }
31752             
31753             file.target.remove();
31754
31755         }, this);
31756         
31757         this.files = [];
31758         
31759         this.refresh();
31760     },
31761     
31762     onClick : function(e, el, o)
31763     {
31764         e.preventDefault();
31765         
31766         this.fireEvent('click', this, o);
31767         
31768     },
31769     
31770     closable : function(closable)
31771     {
31772         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31773             
31774             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31775             
31776             if(closable){
31777                 el.show();
31778                 return;
31779             }
31780             
31781             el.hide();
31782             
31783         }, this);
31784     },
31785     
31786     xhrOnLoad : function(xhr)
31787     {
31788         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31789             el.remove();
31790         }, this);
31791         
31792         if (xhr.readyState !== 4) {
31793             this.arrange();
31794             this.fireEvent('exception', this, xhr);
31795             return;
31796         }
31797
31798         var response = Roo.decode(xhr.responseText);
31799         
31800         if(!response.success){
31801             this.arrange();
31802             this.fireEvent('exception', this, xhr);
31803             return;
31804         }
31805         
31806         var file = this.renderPreview(response.data);
31807         
31808         this.files.push(file);
31809         
31810         this.arrange();
31811         
31812         this.fireEvent('afterupload', this, xhr);
31813         
31814     },
31815     
31816     xhrOnError : function(xhr)
31817     {
31818         Roo.log('xhr on error');
31819         
31820         var response = Roo.decode(xhr.responseText);
31821           
31822         Roo.log(response);
31823         
31824         this.arrange();
31825     },
31826     
31827     process : function(file)
31828     {
31829         if(this.fireEvent('process', this, file) !== false){
31830             if(this.editable && file.type.indexOf('image') != -1){
31831                 this.fireEvent('edit', this, file);
31832                 return;
31833             }
31834
31835             this.uploadStart(file, false);
31836
31837             return;
31838         }
31839         
31840     },
31841     
31842     uploadStart : function(file, crop)
31843     {
31844         this.xhr = new XMLHttpRequest();
31845         
31846         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31847             this.arrange();
31848             return;
31849         }
31850         
31851         file.xhr = this.xhr;
31852             
31853         this.managerEl.createChild({
31854             tag : 'div',
31855             cls : 'roo-document-manager-loading',
31856             cn : [
31857                 {
31858                     tag : 'div',
31859                     tooltip : file.name,
31860                     cls : 'roo-document-manager-thumb',
31861                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31862                 }
31863             ]
31864
31865         });
31866
31867         this.xhr.open(this.method, this.url, true);
31868         
31869         var headers = {
31870             "Accept": "application/json",
31871             "Cache-Control": "no-cache",
31872             "X-Requested-With": "XMLHttpRequest"
31873         };
31874         
31875         for (var headerName in headers) {
31876             var headerValue = headers[headerName];
31877             if (headerValue) {
31878                 this.xhr.setRequestHeader(headerName, headerValue);
31879             }
31880         }
31881         
31882         var _this = this;
31883         
31884         this.xhr.onload = function()
31885         {
31886             _this.xhrOnLoad(_this.xhr);
31887         }
31888         
31889         this.xhr.onerror = function()
31890         {
31891             _this.xhrOnError(_this.xhr);
31892         }
31893         
31894         var formData = new FormData();
31895
31896         formData.append('returnHTML', 'NO');
31897         
31898         if(crop){
31899             formData.append('crop', crop);
31900         }
31901         
31902         formData.append(this.paramName, file, file.name);
31903         
31904         var options = {
31905             file : file, 
31906             manually : false
31907         };
31908         
31909         if(this.fireEvent('prepare', this, formData, options) != false){
31910             
31911             if(options.manually){
31912                 return;
31913             }
31914             
31915             this.xhr.send(formData);
31916             return;
31917         };
31918         
31919         this.uploadCancel();
31920     },
31921     
31922     uploadCancel : function()
31923     {
31924         if (this.xhr) {
31925             this.xhr.abort();
31926         }
31927         
31928         this.delegates = [];
31929         
31930         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31931             el.remove();
31932         }, this);
31933         
31934         this.arrange();
31935     },
31936     
31937     renderPreview : function(file)
31938     {
31939         if(typeof(file.target) != 'undefined' && file.target){
31940             return file;
31941         }
31942         
31943         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31944         
31945         var previewEl = this.managerEl.createChild({
31946             tag : 'div',
31947             cls : 'roo-document-manager-preview',
31948             cn : [
31949                 {
31950                     tag : 'div',
31951                     tooltip : file[this.toolTipName],
31952                     cls : 'roo-document-manager-thumb',
31953                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31954                 },
31955                 {
31956                     tag : 'button',
31957                     cls : 'close',
31958                     html : '<i class="fa fa-times-circle"></i>'
31959                 }
31960             ]
31961         });
31962
31963         var close = previewEl.select('button.close', true).first();
31964
31965         close.on('click', this.onRemove, this, file);
31966
31967         file.target = previewEl;
31968
31969         var image = previewEl.select('img', true).first();
31970         
31971         var _this = this;
31972         
31973         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31974         
31975         image.on('click', this.onClick, this, file);
31976         
31977         this.fireEvent('previewrendered', this, file);
31978         
31979         return file;
31980         
31981     },
31982     
31983     onPreviewLoad : function(file, image)
31984     {
31985         if(typeof(file.target) == 'undefined' || !file.target){
31986             return;
31987         }
31988         
31989         var width = image.dom.naturalWidth || image.dom.width;
31990         var height = image.dom.naturalHeight || image.dom.height;
31991         
31992         if(!this.previewResize) {
31993             return;
31994         }
31995         
31996         if(width > height){
31997             file.target.addClass('wide');
31998             return;
31999         }
32000         
32001         file.target.addClass('tall');
32002         return;
32003         
32004     },
32005     
32006     uploadFromSource : function(file, crop)
32007     {
32008         this.xhr = new XMLHttpRequest();
32009         
32010         this.managerEl.createChild({
32011             tag : 'div',
32012             cls : 'roo-document-manager-loading',
32013             cn : [
32014                 {
32015                     tag : 'div',
32016                     tooltip : file.name,
32017                     cls : 'roo-document-manager-thumb',
32018                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32019                 }
32020             ]
32021
32022         });
32023
32024         this.xhr.open(this.method, this.url, true);
32025         
32026         var headers = {
32027             "Accept": "application/json",
32028             "Cache-Control": "no-cache",
32029             "X-Requested-With": "XMLHttpRequest"
32030         };
32031         
32032         for (var headerName in headers) {
32033             var headerValue = headers[headerName];
32034             if (headerValue) {
32035                 this.xhr.setRequestHeader(headerName, headerValue);
32036             }
32037         }
32038         
32039         var _this = this;
32040         
32041         this.xhr.onload = function()
32042         {
32043             _this.xhrOnLoad(_this.xhr);
32044         }
32045         
32046         this.xhr.onerror = function()
32047         {
32048             _this.xhrOnError(_this.xhr);
32049         }
32050         
32051         var formData = new FormData();
32052
32053         formData.append('returnHTML', 'NO');
32054         
32055         formData.append('crop', crop);
32056         
32057         if(typeof(file.filename) != 'undefined'){
32058             formData.append('filename', file.filename);
32059         }
32060         
32061         if(typeof(file.mimetype) != 'undefined'){
32062             formData.append('mimetype', file.mimetype);
32063         }
32064         
32065         Roo.log(formData);
32066         
32067         if(this.fireEvent('prepare', this, formData) != false){
32068             this.xhr.send(formData);
32069         };
32070     }
32071 });
32072
32073 /*
32074 * Licence: LGPL
32075 */
32076
32077 /**
32078  * @class Roo.bootstrap.DocumentViewer
32079  * @extends Roo.bootstrap.Component
32080  * Bootstrap DocumentViewer class
32081  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32082  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32083  * 
32084  * @constructor
32085  * Create a new DocumentViewer
32086  * @param {Object} config The config object
32087  */
32088
32089 Roo.bootstrap.DocumentViewer = function(config){
32090     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32091     
32092     this.addEvents({
32093         /**
32094          * @event initial
32095          * Fire after initEvent
32096          * @param {Roo.bootstrap.DocumentViewer} this
32097          */
32098         "initial" : true,
32099         /**
32100          * @event click
32101          * Fire after click
32102          * @param {Roo.bootstrap.DocumentViewer} this
32103          */
32104         "click" : true,
32105         /**
32106          * @event download
32107          * Fire after download button
32108          * @param {Roo.bootstrap.DocumentViewer} this
32109          */
32110         "download" : true,
32111         /**
32112          * @event trash
32113          * Fire after trash button
32114          * @param {Roo.bootstrap.DocumentViewer} this
32115          */
32116         "trash" : true
32117         
32118     });
32119 };
32120
32121 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32122     
32123     showDownload : true,
32124     
32125     showTrash : true,
32126     
32127     getAutoCreate : function()
32128     {
32129         var cfg = {
32130             tag : 'div',
32131             cls : 'roo-document-viewer',
32132             cn : [
32133                 {
32134                     tag : 'div',
32135                     cls : 'roo-document-viewer-body',
32136                     cn : [
32137                         {
32138                             tag : 'div',
32139                             cls : 'roo-document-viewer-thumb',
32140                             cn : [
32141                                 {
32142                                     tag : 'img',
32143                                     cls : 'roo-document-viewer-image'
32144                                 }
32145                             ]
32146                         }
32147                     ]
32148                 },
32149                 {
32150                     tag : 'div',
32151                     cls : 'roo-document-viewer-footer',
32152                     cn : {
32153                         tag : 'div',
32154                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32155                         cn : [
32156                             {
32157                                 tag : 'div',
32158                                 cls : 'btn-group roo-document-viewer-download',
32159                                 cn : [
32160                                     {
32161                                         tag : 'button',
32162                                         cls : 'btn btn-default',
32163                                         html : '<i class="fa fa-download"></i>'
32164                                     }
32165                                 ]
32166                             },
32167                             {
32168                                 tag : 'div',
32169                                 cls : 'btn-group roo-document-viewer-trash',
32170                                 cn : [
32171                                     {
32172                                         tag : 'button',
32173                                         cls : 'btn btn-default',
32174                                         html : '<i class="fa fa-trash"></i>'
32175                                     }
32176                                 ]
32177                             }
32178                         ]
32179                     }
32180                 }
32181             ]
32182         };
32183         
32184         return cfg;
32185     },
32186     
32187     initEvents : function()
32188     {
32189         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32190         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32191         
32192         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32193         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32194         
32195         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32196         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32197         
32198         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32199         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32200         
32201         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32202         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32203         
32204         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32205         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32206         
32207         this.bodyEl.on('click', this.onClick, this);
32208         this.downloadBtn.on('click', this.onDownload, this);
32209         this.trashBtn.on('click', this.onTrash, this);
32210         
32211         this.downloadBtn.hide();
32212         this.trashBtn.hide();
32213         
32214         if(this.showDownload){
32215             this.downloadBtn.show();
32216         }
32217         
32218         if(this.showTrash){
32219             this.trashBtn.show();
32220         }
32221         
32222         if(!this.showDownload && !this.showTrash) {
32223             this.footerEl.hide();
32224         }
32225         
32226     },
32227     
32228     initial : function()
32229     {
32230         this.fireEvent('initial', this);
32231         
32232     },
32233     
32234     onClick : function(e)
32235     {
32236         e.preventDefault();
32237         
32238         this.fireEvent('click', this);
32239     },
32240     
32241     onDownload : function(e)
32242     {
32243         e.preventDefault();
32244         
32245         this.fireEvent('download', this);
32246     },
32247     
32248     onTrash : function(e)
32249     {
32250         e.preventDefault();
32251         
32252         this.fireEvent('trash', this);
32253     }
32254     
32255 });
32256 /*
32257  * - LGPL
32258  *
32259  * nav progress bar
32260  * 
32261  */
32262
32263 /**
32264  * @class Roo.bootstrap.NavProgressBar
32265  * @extends Roo.bootstrap.Component
32266  * Bootstrap NavProgressBar class
32267  * 
32268  * @constructor
32269  * Create a new nav progress bar
32270  * @param {Object} config The config object
32271  */
32272
32273 Roo.bootstrap.NavProgressBar = function(config){
32274     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32275
32276     this.bullets = this.bullets || [];
32277    
32278 //    Roo.bootstrap.NavProgressBar.register(this);
32279      this.addEvents({
32280         /**
32281              * @event changed
32282              * Fires when the active item changes
32283              * @param {Roo.bootstrap.NavProgressBar} this
32284              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32285              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32286          */
32287         'changed': true
32288      });
32289     
32290 };
32291
32292 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32293     
32294     bullets : [],
32295     barItems : [],
32296     
32297     getAutoCreate : function()
32298     {
32299         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32300         
32301         cfg = {
32302             tag : 'div',
32303             cls : 'roo-navigation-bar-group',
32304             cn : [
32305                 {
32306                     tag : 'div',
32307                     cls : 'roo-navigation-top-bar'
32308                 },
32309                 {
32310                     tag : 'div',
32311                     cls : 'roo-navigation-bullets-bar',
32312                     cn : [
32313                         {
32314                             tag : 'ul',
32315                             cls : 'roo-navigation-bar'
32316                         }
32317                     ]
32318                 },
32319                 
32320                 {
32321                     tag : 'div',
32322                     cls : 'roo-navigation-bottom-bar'
32323                 }
32324             ]
32325             
32326         };
32327         
32328         return cfg;
32329         
32330     },
32331     
32332     initEvents: function() 
32333     {
32334         
32335     },
32336     
32337     onRender : function(ct, position) 
32338     {
32339         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32340         
32341         if(this.bullets.length){
32342             Roo.each(this.bullets, function(b){
32343                this.addItem(b);
32344             }, this);
32345         }
32346         
32347         this.format();
32348         
32349     },
32350     
32351     addItem : function(cfg)
32352     {
32353         var item = new Roo.bootstrap.NavProgressItem(cfg);
32354         
32355         item.parentId = this.id;
32356         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32357         
32358         if(cfg.html){
32359             var top = new Roo.bootstrap.Element({
32360                 tag : 'div',
32361                 cls : 'roo-navigation-bar-text'
32362             });
32363             
32364             var bottom = new Roo.bootstrap.Element({
32365                 tag : 'div',
32366                 cls : 'roo-navigation-bar-text'
32367             });
32368             
32369             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32370             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32371             
32372             var topText = new Roo.bootstrap.Element({
32373                 tag : 'span',
32374                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32375             });
32376             
32377             var bottomText = new Roo.bootstrap.Element({
32378                 tag : 'span',
32379                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32380             });
32381             
32382             topText.onRender(top.el, null);
32383             bottomText.onRender(bottom.el, null);
32384             
32385             item.topEl = top;
32386             item.bottomEl = bottom;
32387         }
32388         
32389         this.barItems.push(item);
32390         
32391         return item;
32392     },
32393     
32394     getActive : function()
32395     {
32396         var active = false;
32397         
32398         Roo.each(this.barItems, function(v){
32399             
32400             if (!v.isActive()) {
32401                 return;
32402             }
32403             
32404             active = v;
32405             return false;
32406             
32407         });
32408         
32409         return active;
32410     },
32411     
32412     setActiveItem : function(item)
32413     {
32414         var prev = false;
32415         
32416         Roo.each(this.barItems, function(v){
32417             if (v.rid == item.rid) {
32418                 return ;
32419             }
32420             
32421             if (v.isActive()) {
32422                 v.setActive(false);
32423                 prev = v;
32424             }
32425         });
32426
32427         item.setActive(true);
32428         
32429         this.fireEvent('changed', this, item, prev);
32430     },
32431     
32432     getBarItem: function(rid)
32433     {
32434         var ret = false;
32435         
32436         Roo.each(this.barItems, function(e) {
32437             if (e.rid != rid) {
32438                 return;
32439             }
32440             
32441             ret =  e;
32442             return false;
32443         });
32444         
32445         return ret;
32446     },
32447     
32448     indexOfItem : function(item)
32449     {
32450         var index = false;
32451         
32452         Roo.each(this.barItems, function(v, i){
32453             
32454             if (v.rid != item.rid) {
32455                 return;
32456             }
32457             
32458             index = i;
32459             return false
32460         });
32461         
32462         return index;
32463     },
32464     
32465     setActiveNext : function()
32466     {
32467         var i = this.indexOfItem(this.getActive());
32468         
32469         if (i > this.barItems.length) {
32470             return;
32471         }
32472         
32473         this.setActiveItem(this.barItems[i+1]);
32474     },
32475     
32476     setActivePrev : function()
32477     {
32478         var i = this.indexOfItem(this.getActive());
32479         
32480         if (i  < 1) {
32481             return;
32482         }
32483         
32484         this.setActiveItem(this.barItems[i-1]);
32485     },
32486     
32487     format : function()
32488     {
32489         if(!this.barItems.length){
32490             return;
32491         }
32492      
32493         var width = 100 / this.barItems.length;
32494         
32495         Roo.each(this.barItems, function(i){
32496             i.el.setStyle('width', width + '%');
32497             i.topEl.el.setStyle('width', width + '%');
32498             i.bottomEl.el.setStyle('width', width + '%');
32499         }, this);
32500         
32501     }
32502     
32503 });
32504 /*
32505  * - LGPL
32506  *
32507  * Nav Progress Item
32508  * 
32509  */
32510
32511 /**
32512  * @class Roo.bootstrap.NavProgressItem
32513  * @extends Roo.bootstrap.Component
32514  * Bootstrap NavProgressItem class
32515  * @cfg {String} rid the reference id
32516  * @cfg {Boolean} active (true|false) Is item active default false
32517  * @cfg {Boolean} disabled (true|false) Is item active default false
32518  * @cfg {String} html
32519  * @cfg {String} position (top|bottom) text position default bottom
32520  * @cfg {String} icon show icon instead of number
32521  * 
32522  * @constructor
32523  * Create a new NavProgressItem
32524  * @param {Object} config The config object
32525  */
32526 Roo.bootstrap.NavProgressItem = function(config){
32527     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32528     this.addEvents({
32529         // raw events
32530         /**
32531          * @event click
32532          * The raw click event for the entire grid.
32533          * @param {Roo.bootstrap.NavProgressItem} this
32534          * @param {Roo.EventObject} e
32535          */
32536         "click" : true
32537     });
32538    
32539 };
32540
32541 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32542     
32543     rid : '',
32544     active : false,
32545     disabled : false,
32546     html : '',
32547     position : 'bottom',
32548     icon : false,
32549     
32550     getAutoCreate : function()
32551     {
32552         var iconCls = 'roo-navigation-bar-item-icon';
32553         
32554         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32555         
32556         var cfg = {
32557             tag: 'li',
32558             cls: 'roo-navigation-bar-item',
32559             cn : [
32560                 {
32561                     tag : 'i',
32562                     cls : iconCls
32563                 }
32564             ]
32565         };
32566         
32567         if(this.active){
32568             cfg.cls += ' active';
32569         }
32570         if(this.disabled){
32571             cfg.cls += ' disabled';
32572         }
32573         
32574         return cfg;
32575     },
32576     
32577     disable : function()
32578     {
32579         this.setDisabled(true);
32580     },
32581     
32582     enable : function()
32583     {
32584         this.setDisabled(false);
32585     },
32586     
32587     initEvents: function() 
32588     {
32589         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32590         
32591         this.iconEl.on('click', this.onClick, this);
32592     },
32593     
32594     onClick : function(e)
32595     {
32596         e.preventDefault();
32597         
32598         if(this.disabled){
32599             return;
32600         }
32601         
32602         if(this.fireEvent('click', this, e) === false){
32603             return;
32604         };
32605         
32606         this.parent().setActiveItem(this);
32607     },
32608     
32609     isActive: function () 
32610     {
32611         return this.active;
32612     },
32613     
32614     setActive : function(state)
32615     {
32616         if(this.active == state){
32617             return;
32618         }
32619         
32620         this.active = state;
32621         
32622         if (state) {
32623             this.el.addClass('active');
32624             return;
32625         }
32626         
32627         this.el.removeClass('active');
32628         
32629         return;
32630     },
32631     
32632     setDisabled : function(state)
32633     {
32634         if(this.disabled == state){
32635             return;
32636         }
32637         
32638         this.disabled = state;
32639         
32640         if (state) {
32641             this.el.addClass('disabled');
32642             return;
32643         }
32644         
32645         this.el.removeClass('disabled');
32646     },
32647     
32648     tooltipEl : function()
32649     {
32650         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32651     }
32652 });
32653  
32654
32655  /*
32656  * - LGPL
32657  *
32658  * FieldLabel
32659  * 
32660  */
32661
32662 /**
32663  * @class Roo.bootstrap.FieldLabel
32664  * @extends Roo.bootstrap.Component
32665  * Bootstrap FieldLabel class
32666  * @cfg {String} html contents of the element
32667  * @cfg {String} tag tag of the element default label
32668  * @cfg {String} cls class of the element
32669  * @cfg {String} target label target 
32670  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32671  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32672  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32673  * @cfg {String} iconTooltip default "This field is required"
32674  * @cfg {String} indicatorpos (left|right) default left
32675  * 
32676  * @constructor
32677  * Create a new FieldLabel
32678  * @param {Object} config The config object
32679  */
32680
32681 Roo.bootstrap.FieldLabel = function(config){
32682     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32683     
32684     this.addEvents({
32685             /**
32686              * @event invalid
32687              * Fires after the field has been marked as invalid.
32688              * @param {Roo.form.FieldLabel} this
32689              * @param {String} msg The validation message
32690              */
32691             invalid : true,
32692             /**
32693              * @event valid
32694              * Fires after the field has been validated with no errors.
32695              * @param {Roo.form.FieldLabel} this
32696              */
32697             valid : true
32698         });
32699 };
32700
32701 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32702     
32703     tag: 'label',
32704     cls: '',
32705     html: '',
32706     target: '',
32707     allowBlank : true,
32708     invalidClass : 'has-warning',
32709     validClass : 'has-success',
32710     iconTooltip : 'This field is required',
32711     indicatorpos : 'left',
32712     
32713     getAutoCreate : function(){
32714         
32715         var cls = "";
32716         if (!this.allowBlank) {
32717             cls  = "visible";
32718         }
32719         
32720         var cfg = {
32721             tag : this.tag,
32722             cls : 'roo-bootstrap-field-label ' + this.cls,
32723             for : this.target,
32724             cn : [
32725                 {
32726                     tag : 'i',
32727                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32728                     tooltip : this.iconTooltip
32729                 },
32730                 {
32731                     tag : 'span',
32732                     html : this.html
32733                 }
32734             ] 
32735         };
32736         
32737         if(this.indicatorpos == 'right'){
32738             var cfg = {
32739                 tag : this.tag,
32740                 cls : 'roo-bootstrap-field-label ' + this.cls,
32741                 for : this.target,
32742                 cn : [
32743                     {
32744                         tag : 'span',
32745                         html : this.html
32746                     },
32747                     {
32748                         tag : 'i',
32749                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32750                         tooltip : this.iconTooltip
32751                     }
32752                 ] 
32753             };
32754         }
32755         
32756         return cfg;
32757     },
32758     
32759     initEvents: function() 
32760     {
32761         Roo.bootstrap.Element.superclass.initEvents.call(this);
32762         
32763         this.indicator = this.indicatorEl();
32764         
32765         if(this.indicator){
32766             this.indicator.removeClass('visible');
32767             this.indicator.addClass('invisible');
32768         }
32769         
32770         Roo.bootstrap.FieldLabel.register(this);
32771     },
32772     
32773     indicatorEl : function()
32774     {
32775         var indicator = this.el.select('i.roo-required-indicator',true).first();
32776         
32777         if(!indicator){
32778             return false;
32779         }
32780         
32781         return indicator;
32782         
32783     },
32784     
32785     /**
32786      * Mark this field as valid
32787      */
32788     markValid : function()
32789     {
32790         if(this.indicator){
32791             this.indicator.removeClass('visible');
32792             this.indicator.addClass('invisible');
32793         }
32794         if (Roo.bootstrap.version == 3) {
32795             this.el.removeClass(this.invalidClass);
32796             this.el.addClass(this.validClass);
32797         } else {
32798             this.el.removeClass('is-invalid');
32799             this.el.addClass('is-valid');
32800         }
32801         
32802         
32803         this.fireEvent('valid', this);
32804     },
32805     
32806     /**
32807      * Mark this field as invalid
32808      * @param {String} msg The validation message
32809      */
32810     markInvalid : function(msg)
32811     {
32812         if(this.indicator){
32813             this.indicator.removeClass('invisible');
32814             this.indicator.addClass('visible');
32815         }
32816           if (Roo.bootstrap.version == 3) {
32817             this.el.removeClass(this.validClass);
32818             this.el.addClass(this.invalidClass);
32819         } else {
32820             this.el.removeClass('is-valid');
32821             this.el.addClass('is-invalid');
32822         }
32823         
32824         
32825         this.fireEvent('invalid', this, msg);
32826     }
32827     
32828    
32829 });
32830
32831 Roo.apply(Roo.bootstrap.FieldLabel, {
32832     
32833     groups: {},
32834     
32835      /**
32836     * register a FieldLabel Group
32837     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32838     */
32839     register : function(label)
32840     {
32841         if(this.groups.hasOwnProperty(label.target)){
32842             return;
32843         }
32844      
32845         this.groups[label.target] = label;
32846         
32847     },
32848     /**
32849     * fetch a FieldLabel Group based on the target
32850     * @param {string} target
32851     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32852     */
32853     get: function(target) {
32854         if (typeof(this.groups[target]) == 'undefined') {
32855             return false;
32856         }
32857         
32858         return this.groups[target] ;
32859     }
32860 });
32861
32862  
32863
32864  /*
32865  * - LGPL
32866  *
32867  * page DateSplitField.
32868  * 
32869  */
32870
32871
32872 /**
32873  * @class Roo.bootstrap.DateSplitField
32874  * @extends Roo.bootstrap.Component
32875  * Bootstrap DateSplitField class
32876  * @cfg {string} fieldLabel - the label associated
32877  * @cfg {Number} labelWidth set the width of label (0-12)
32878  * @cfg {String} labelAlign (top|left)
32879  * @cfg {Boolean} dayAllowBlank (true|false) default false
32880  * @cfg {Boolean} monthAllowBlank (true|false) default false
32881  * @cfg {Boolean} yearAllowBlank (true|false) default false
32882  * @cfg {string} dayPlaceholder 
32883  * @cfg {string} monthPlaceholder
32884  * @cfg {string} yearPlaceholder
32885  * @cfg {string} dayFormat default 'd'
32886  * @cfg {string} monthFormat default 'm'
32887  * @cfg {string} yearFormat default 'Y'
32888  * @cfg {Number} labellg set the width of label (1-12)
32889  * @cfg {Number} labelmd set the width of label (1-12)
32890  * @cfg {Number} labelsm set the width of label (1-12)
32891  * @cfg {Number} labelxs set the width of label (1-12)
32892
32893  *     
32894  * @constructor
32895  * Create a new DateSplitField
32896  * @param {Object} config The config object
32897  */
32898
32899 Roo.bootstrap.DateSplitField = function(config){
32900     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32901     
32902     this.addEvents({
32903         // raw events
32904          /**
32905          * @event years
32906          * getting the data of years
32907          * @param {Roo.bootstrap.DateSplitField} this
32908          * @param {Object} years
32909          */
32910         "years" : true,
32911         /**
32912          * @event days
32913          * getting the data of days
32914          * @param {Roo.bootstrap.DateSplitField} this
32915          * @param {Object} days
32916          */
32917         "days" : true,
32918         /**
32919          * @event invalid
32920          * Fires after the field has been marked as invalid.
32921          * @param {Roo.form.Field} this
32922          * @param {String} msg The validation message
32923          */
32924         invalid : true,
32925        /**
32926          * @event valid
32927          * Fires after the field has been validated with no errors.
32928          * @param {Roo.form.Field} this
32929          */
32930         valid : true
32931     });
32932 };
32933
32934 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32935     
32936     fieldLabel : '',
32937     labelAlign : 'top',
32938     labelWidth : 3,
32939     dayAllowBlank : false,
32940     monthAllowBlank : false,
32941     yearAllowBlank : false,
32942     dayPlaceholder : '',
32943     monthPlaceholder : '',
32944     yearPlaceholder : '',
32945     dayFormat : 'd',
32946     monthFormat : 'm',
32947     yearFormat : 'Y',
32948     isFormField : true,
32949     labellg : 0,
32950     labelmd : 0,
32951     labelsm : 0,
32952     labelxs : 0,
32953     
32954     getAutoCreate : function()
32955     {
32956         var cfg = {
32957             tag : 'div',
32958             cls : 'row roo-date-split-field-group',
32959             cn : [
32960                 {
32961                     tag : 'input',
32962                     type : 'hidden',
32963                     cls : 'form-hidden-field roo-date-split-field-group-value',
32964                     name : this.name
32965                 }
32966             ]
32967         };
32968         
32969         var labelCls = 'col-md-12';
32970         var contentCls = 'col-md-4';
32971         
32972         if(this.fieldLabel){
32973             
32974             var label = {
32975                 tag : 'div',
32976                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32977                 cn : [
32978                     {
32979                         tag : 'label',
32980                         html : this.fieldLabel
32981                     }
32982                 ]
32983             };
32984             
32985             if(this.labelAlign == 'left'){
32986             
32987                 if(this.labelWidth > 12){
32988                     label.style = "width: " + this.labelWidth + 'px';
32989                 }
32990
32991                 if(this.labelWidth < 13 && this.labelmd == 0){
32992                     this.labelmd = this.labelWidth;
32993                 }
32994
32995                 if(this.labellg > 0){
32996                     labelCls = ' col-lg-' + this.labellg;
32997                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32998                 }
32999
33000                 if(this.labelmd > 0){
33001                     labelCls = ' col-md-' + this.labelmd;
33002                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33003                 }
33004
33005                 if(this.labelsm > 0){
33006                     labelCls = ' col-sm-' + this.labelsm;
33007                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33008                 }
33009
33010                 if(this.labelxs > 0){
33011                     labelCls = ' col-xs-' + this.labelxs;
33012                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33013                 }
33014             }
33015             
33016             label.cls += ' ' + labelCls;
33017             
33018             cfg.cn.push(label);
33019         }
33020         
33021         Roo.each(['day', 'month', 'year'], function(t){
33022             cfg.cn.push({
33023                 tag : 'div',
33024                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33025             });
33026         }, this);
33027         
33028         return cfg;
33029     },
33030     
33031     inputEl: function ()
33032     {
33033         return this.el.select('.roo-date-split-field-group-value', true).first();
33034     },
33035     
33036     onRender : function(ct, position) 
33037     {
33038         var _this = this;
33039         
33040         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33041         
33042         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33043         
33044         this.dayField = new Roo.bootstrap.ComboBox({
33045             allowBlank : this.dayAllowBlank,
33046             alwaysQuery : true,
33047             displayField : 'value',
33048             editable : false,
33049             fieldLabel : '',
33050             forceSelection : true,
33051             mode : 'local',
33052             placeholder : this.dayPlaceholder,
33053             selectOnFocus : true,
33054             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33055             triggerAction : 'all',
33056             typeAhead : true,
33057             valueField : 'value',
33058             store : new Roo.data.SimpleStore({
33059                 data : (function() {    
33060                     var days = [];
33061                     _this.fireEvent('days', _this, days);
33062                     return days;
33063                 })(),
33064                 fields : [ 'value' ]
33065             }),
33066             listeners : {
33067                 select : function (_self, record, index)
33068                 {
33069                     _this.setValue(_this.getValue());
33070                 }
33071             }
33072         });
33073
33074         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33075         
33076         this.monthField = new Roo.bootstrap.MonthField({
33077             after : '<i class=\"fa fa-calendar\"></i>',
33078             allowBlank : this.monthAllowBlank,
33079             placeholder : this.monthPlaceholder,
33080             readOnly : true,
33081             listeners : {
33082                 render : function (_self)
33083                 {
33084                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33085                         e.preventDefault();
33086                         _self.focus();
33087                     });
33088                 },
33089                 select : function (_self, oldvalue, newvalue)
33090                 {
33091                     _this.setValue(_this.getValue());
33092                 }
33093             }
33094         });
33095         
33096         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33097         
33098         this.yearField = new Roo.bootstrap.ComboBox({
33099             allowBlank : this.yearAllowBlank,
33100             alwaysQuery : true,
33101             displayField : 'value',
33102             editable : false,
33103             fieldLabel : '',
33104             forceSelection : true,
33105             mode : 'local',
33106             placeholder : this.yearPlaceholder,
33107             selectOnFocus : true,
33108             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33109             triggerAction : 'all',
33110             typeAhead : true,
33111             valueField : 'value',
33112             store : new Roo.data.SimpleStore({
33113                 data : (function() {
33114                     var years = [];
33115                     _this.fireEvent('years', _this, years);
33116                     return years;
33117                 })(),
33118                 fields : [ 'value' ]
33119             }),
33120             listeners : {
33121                 select : function (_self, record, index)
33122                 {
33123                     _this.setValue(_this.getValue());
33124                 }
33125             }
33126         });
33127
33128         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33129     },
33130     
33131     setValue : function(v, format)
33132     {
33133         this.inputEl.dom.value = v;
33134         
33135         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33136         
33137         var d = Date.parseDate(v, f);
33138         
33139         if(!d){
33140             this.validate();
33141             return;
33142         }
33143         
33144         this.setDay(d.format(this.dayFormat));
33145         this.setMonth(d.format(this.monthFormat));
33146         this.setYear(d.format(this.yearFormat));
33147         
33148         this.validate();
33149         
33150         return;
33151     },
33152     
33153     setDay : function(v)
33154     {
33155         this.dayField.setValue(v);
33156         this.inputEl.dom.value = this.getValue();
33157         this.validate();
33158         return;
33159     },
33160     
33161     setMonth : function(v)
33162     {
33163         this.monthField.setValue(v, true);
33164         this.inputEl.dom.value = this.getValue();
33165         this.validate();
33166         return;
33167     },
33168     
33169     setYear : function(v)
33170     {
33171         this.yearField.setValue(v);
33172         this.inputEl.dom.value = this.getValue();
33173         this.validate();
33174         return;
33175     },
33176     
33177     getDay : function()
33178     {
33179         return this.dayField.getValue();
33180     },
33181     
33182     getMonth : function()
33183     {
33184         return this.monthField.getValue();
33185     },
33186     
33187     getYear : function()
33188     {
33189         return this.yearField.getValue();
33190     },
33191     
33192     getValue : function()
33193     {
33194         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33195         
33196         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33197         
33198         return date;
33199     },
33200     
33201     reset : function()
33202     {
33203         this.setDay('');
33204         this.setMonth('');
33205         this.setYear('');
33206         this.inputEl.dom.value = '';
33207         this.validate();
33208         return;
33209     },
33210     
33211     validate : function()
33212     {
33213         var d = this.dayField.validate();
33214         var m = this.monthField.validate();
33215         var y = this.yearField.validate();
33216         
33217         var valid = true;
33218         
33219         if(
33220                 (!this.dayAllowBlank && !d) ||
33221                 (!this.monthAllowBlank && !m) ||
33222                 (!this.yearAllowBlank && !y)
33223         ){
33224             valid = false;
33225         }
33226         
33227         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33228             return valid;
33229         }
33230         
33231         if(valid){
33232             this.markValid();
33233             return valid;
33234         }
33235         
33236         this.markInvalid();
33237         
33238         return valid;
33239     },
33240     
33241     markValid : function()
33242     {
33243         
33244         var label = this.el.select('label', true).first();
33245         var icon = this.el.select('i.fa-star', true).first();
33246
33247         if(label && icon){
33248             icon.remove();
33249         }
33250         
33251         this.fireEvent('valid', this);
33252     },
33253     
33254      /**
33255      * Mark this field as invalid
33256      * @param {String} msg The validation message
33257      */
33258     markInvalid : function(msg)
33259     {
33260         
33261         var label = this.el.select('label', true).first();
33262         var icon = this.el.select('i.fa-star', true).first();
33263
33264         if(label && !icon){
33265             this.el.select('.roo-date-split-field-label', true).createChild({
33266                 tag : 'i',
33267                 cls : 'text-danger fa fa-lg fa-star',
33268                 tooltip : 'This field is required',
33269                 style : 'margin-right:5px;'
33270             }, label, true);
33271         }
33272         
33273         this.fireEvent('invalid', this, msg);
33274     },
33275     
33276     clearInvalid : function()
33277     {
33278         var label = this.el.select('label', true).first();
33279         var icon = this.el.select('i.fa-star', true).first();
33280
33281         if(label && icon){
33282             icon.remove();
33283         }
33284         
33285         this.fireEvent('valid', this);
33286     },
33287     
33288     getName: function()
33289     {
33290         return this.name;
33291     }
33292     
33293 });
33294
33295  /**
33296  *
33297  * This is based on 
33298  * http://masonry.desandro.com
33299  *
33300  * The idea is to render all the bricks based on vertical width...
33301  *
33302  * The original code extends 'outlayer' - we might need to use that....
33303  * 
33304  */
33305
33306
33307 /**
33308  * @class Roo.bootstrap.LayoutMasonry
33309  * @extends Roo.bootstrap.Component
33310  * Bootstrap Layout Masonry class
33311  * 
33312  * @constructor
33313  * Create a new Element
33314  * @param {Object} config The config object
33315  */
33316
33317 Roo.bootstrap.LayoutMasonry = function(config){
33318     
33319     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33320     
33321     this.bricks = [];
33322     
33323     Roo.bootstrap.LayoutMasonry.register(this);
33324     
33325     this.addEvents({
33326         // raw events
33327         /**
33328          * @event layout
33329          * Fire after layout the items
33330          * @param {Roo.bootstrap.LayoutMasonry} this
33331          * @param {Roo.EventObject} e
33332          */
33333         "layout" : true
33334     });
33335     
33336 };
33337
33338 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33339     
33340     /**
33341      * @cfg {Boolean} isLayoutInstant = no animation?
33342      */   
33343     isLayoutInstant : false, // needed?
33344    
33345     /**
33346      * @cfg {Number} boxWidth  width of the columns
33347      */   
33348     boxWidth : 450,
33349     
33350       /**
33351      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33352      */   
33353     boxHeight : 0,
33354     
33355     /**
33356      * @cfg {Number} padWidth padding below box..
33357      */   
33358     padWidth : 10, 
33359     
33360     /**
33361      * @cfg {Number} gutter gutter width..
33362      */   
33363     gutter : 10,
33364     
33365      /**
33366      * @cfg {Number} maxCols maximum number of columns
33367      */   
33368     
33369     maxCols: 0,
33370     
33371     /**
33372      * @cfg {Boolean} isAutoInitial defalut true
33373      */   
33374     isAutoInitial : true, 
33375     
33376     containerWidth: 0,
33377     
33378     /**
33379      * @cfg {Boolean} isHorizontal defalut false
33380      */   
33381     isHorizontal : false, 
33382
33383     currentSize : null,
33384     
33385     tag: 'div',
33386     
33387     cls: '',
33388     
33389     bricks: null, //CompositeElement
33390     
33391     cols : 1,
33392     
33393     _isLayoutInited : false,
33394     
33395 //    isAlternative : false, // only use for vertical layout...
33396     
33397     /**
33398      * @cfg {Number} alternativePadWidth padding below box..
33399      */   
33400     alternativePadWidth : 50,
33401     
33402     selectedBrick : [],
33403     
33404     getAutoCreate : function(){
33405         
33406         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33407         
33408         var cfg = {
33409             tag: this.tag,
33410             cls: 'blog-masonary-wrapper ' + this.cls,
33411             cn : {
33412                 cls : 'mas-boxes masonary'
33413             }
33414         };
33415         
33416         return cfg;
33417     },
33418     
33419     getChildContainer: function( )
33420     {
33421         if (this.boxesEl) {
33422             return this.boxesEl;
33423         }
33424         
33425         this.boxesEl = this.el.select('.mas-boxes').first();
33426         
33427         return this.boxesEl;
33428     },
33429     
33430     
33431     initEvents : function()
33432     {
33433         var _this = this;
33434         
33435         if(this.isAutoInitial){
33436             Roo.log('hook children rendered');
33437             this.on('childrenrendered', function() {
33438                 Roo.log('children rendered');
33439                 _this.initial();
33440             } ,this);
33441         }
33442     },
33443     
33444     initial : function()
33445     {
33446         this.selectedBrick = [];
33447         
33448         this.currentSize = this.el.getBox(true);
33449         
33450         Roo.EventManager.onWindowResize(this.resize, this); 
33451
33452         if(!this.isAutoInitial){
33453             this.layout();
33454             return;
33455         }
33456         
33457         this.layout();
33458         
33459         return;
33460         //this.layout.defer(500,this);
33461         
33462     },
33463     
33464     resize : function()
33465     {
33466         var cs = this.el.getBox(true);
33467         
33468         if (
33469                 this.currentSize.width == cs.width && 
33470                 this.currentSize.x == cs.x && 
33471                 this.currentSize.height == cs.height && 
33472                 this.currentSize.y == cs.y 
33473         ) {
33474             Roo.log("no change in with or X or Y");
33475             return;
33476         }
33477         
33478         this.currentSize = cs;
33479         
33480         this.layout();
33481         
33482     },
33483     
33484     layout : function()
33485     {   
33486         this._resetLayout();
33487         
33488         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33489         
33490         this.layoutItems( isInstant );
33491       
33492         this._isLayoutInited = true;
33493         
33494         this.fireEvent('layout', this);
33495         
33496     },
33497     
33498     _resetLayout : function()
33499     {
33500         if(this.isHorizontal){
33501             this.horizontalMeasureColumns();
33502             return;
33503         }
33504         
33505         this.verticalMeasureColumns();
33506         
33507     },
33508     
33509     verticalMeasureColumns : function()
33510     {
33511         this.getContainerWidth();
33512         
33513 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33514 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33515 //            return;
33516 //        }
33517         
33518         var boxWidth = this.boxWidth + this.padWidth;
33519         
33520         if(this.containerWidth < this.boxWidth){
33521             boxWidth = this.containerWidth
33522         }
33523         
33524         var containerWidth = this.containerWidth;
33525         
33526         var cols = Math.floor(containerWidth / boxWidth);
33527         
33528         this.cols = Math.max( cols, 1 );
33529         
33530         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33531         
33532         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33533         
33534         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33535         
33536         this.colWidth = boxWidth + avail - this.padWidth;
33537         
33538         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33539         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33540     },
33541     
33542     horizontalMeasureColumns : function()
33543     {
33544         this.getContainerWidth();
33545         
33546         var boxWidth = this.boxWidth;
33547         
33548         if(this.containerWidth < boxWidth){
33549             boxWidth = this.containerWidth;
33550         }
33551         
33552         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33553         
33554         this.el.setHeight(boxWidth);
33555         
33556     },
33557     
33558     getContainerWidth : function()
33559     {
33560         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33561     },
33562     
33563     layoutItems : function( isInstant )
33564     {
33565         Roo.log(this.bricks);
33566         
33567         var items = Roo.apply([], this.bricks);
33568         
33569         if(this.isHorizontal){
33570             this._horizontalLayoutItems( items , isInstant );
33571             return;
33572         }
33573         
33574 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33575 //            this._verticalAlternativeLayoutItems( items , isInstant );
33576 //            return;
33577 //        }
33578         
33579         this._verticalLayoutItems( items , isInstant );
33580         
33581     },
33582     
33583     _verticalLayoutItems : function ( items , isInstant)
33584     {
33585         if ( !items || !items.length ) {
33586             return;
33587         }
33588         
33589         var standard = [
33590             ['xs', 'xs', 'xs', 'tall'],
33591             ['xs', 'xs', 'tall'],
33592             ['xs', 'xs', 'sm'],
33593             ['xs', 'xs', 'xs'],
33594             ['xs', 'tall'],
33595             ['xs', 'sm'],
33596             ['xs', 'xs'],
33597             ['xs'],
33598             
33599             ['sm', 'xs', 'xs'],
33600             ['sm', 'xs'],
33601             ['sm'],
33602             
33603             ['tall', 'xs', 'xs', 'xs'],
33604             ['tall', 'xs', 'xs'],
33605             ['tall', 'xs'],
33606             ['tall']
33607             
33608         ];
33609         
33610         var queue = [];
33611         
33612         var boxes = [];
33613         
33614         var box = [];
33615         
33616         Roo.each(items, function(item, k){
33617             
33618             switch (item.size) {
33619                 // these layouts take up a full box,
33620                 case 'md' :
33621                 case 'md-left' :
33622                 case 'md-right' :
33623                 case 'wide' :
33624                     
33625                     if(box.length){
33626                         boxes.push(box);
33627                         box = [];
33628                     }
33629                     
33630                     boxes.push([item]);
33631                     
33632                     break;
33633                     
33634                 case 'xs' :
33635                 case 'sm' :
33636                 case 'tall' :
33637                     
33638                     box.push(item);
33639                     
33640                     break;
33641                 default :
33642                     break;
33643                     
33644             }
33645             
33646         }, this);
33647         
33648         if(box.length){
33649             boxes.push(box);
33650             box = [];
33651         }
33652         
33653         var filterPattern = function(box, length)
33654         {
33655             if(!box.length){
33656                 return;
33657             }
33658             
33659             var match = false;
33660             
33661             var pattern = box.slice(0, length);
33662             
33663             var format = [];
33664             
33665             Roo.each(pattern, function(i){
33666                 format.push(i.size);
33667             }, this);
33668             
33669             Roo.each(standard, function(s){
33670                 
33671                 if(String(s) != String(format)){
33672                     return;
33673                 }
33674                 
33675                 match = true;
33676                 return false;
33677                 
33678             }, this);
33679             
33680             if(!match && length == 1){
33681                 return;
33682             }
33683             
33684             if(!match){
33685                 filterPattern(box, length - 1);
33686                 return;
33687             }
33688                 
33689             queue.push(pattern);
33690
33691             box = box.slice(length, box.length);
33692
33693             filterPattern(box, 4);
33694
33695             return;
33696             
33697         }
33698         
33699         Roo.each(boxes, function(box, k){
33700             
33701             if(!box.length){
33702                 return;
33703             }
33704             
33705             if(box.length == 1){
33706                 queue.push(box);
33707                 return;
33708             }
33709             
33710             filterPattern(box, 4);
33711             
33712         }, this);
33713         
33714         this._processVerticalLayoutQueue( queue, isInstant );
33715         
33716     },
33717     
33718 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33719 //    {
33720 //        if ( !items || !items.length ) {
33721 //            return;
33722 //        }
33723 //
33724 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33725 //        
33726 //    },
33727     
33728     _horizontalLayoutItems : function ( items , isInstant)
33729     {
33730         if ( !items || !items.length || items.length < 3) {
33731             return;
33732         }
33733         
33734         items.reverse();
33735         
33736         var eItems = items.slice(0, 3);
33737         
33738         items = items.slice(3, items.length);
33739         
33740         var standard = [
33741             ['xs', 'xs', 'xs', 'wide'],
33742             ['xs', 'xs', 'wide'],
33743             ['xs', 'xs', 'sm'],
33744             ['xs', 'xs', 'xs'],
33745             ['xs', 'wide'],
33746             ['xs', 'sm'],
33747             ['xs', 'xs'],
33748             ['xs'],
33749             
33750             ['sm', 'xs', 'xs'],
33751             ['sm', 'xs'],
33752             ['sm'],
33753             
33754             ['wide', 'xs', 'xs', 'xs'],
33755             ['wide', 'xs', 'xs'],
33756             ['wide', 'xs'],
33757             ['wide'],
33758             
33759             ['wide-thin']
33760         ];
33761         
33762         var queue = [];
33763         
33764         var boxes = [];
33765         
33766         var box = [];
33767         
33768         Roo.each(items, function(item, k){
33769             
33770             switch (item.size) {
33771                 case 'md' :
33772                 case 'md-left' :
33773                 case 'md-right' :
33774                 case 'tall' :
33775                     
33776                     if(box.length){
33777                         boxes.push(box);
33778                         box = [];
33779                     }
33780                     
33781                     boxes.push([item]);
33782                     
33783                     break;
33784                     
33785                 case 'xs' :
33786                 case 'sm' :
33787                 case 'wide' :
33788                 case 'wide-thin' :
33789                     
33790                     box.push(item);
33791                     
33792                     break;
33793                 default :
33794                     break;
33795                     
33796             }
33797             
33798         }, this);
33799         
33800         if(box.length){
33801             boxes.push(box);
33802             box = [];
33803         }
33804         
33805         var filterPattern = function(box, length)
33806         {
33807             if(!box.length){
33808                 return;
33809             }
33810             
33811             var match = false;
33812             
33813             var pattern = box.slice(0, length);
33814             
33815             var format = [];
33816             
33817             Roo.each(pattern, function(i){
33818                 format.push(i.size);
33819             }, this);
33820             
33821             Roo.each(standard, function(s){
33822                 
33823                 if(String(s) != String(format)){
33824                     return;
33825                 }
33826                 
33827                 match = true;
33828                 return false;
33829                 
33830             }, this);
33831             
33832             if(!match && length == 1){
33833                 return;
33834             }
33835             
33836             if(!match){
33837                 filterPattern(box, length - 1);
33838                 return;
33839             }
33840                 
33841             queue.push(pattern);
33842
33843             box = box.slice(length, box.length);
33844
33845             filterPattern(box, 4);
33846
33847             return;
33848             
33849         }
33850         
33851         Roo.each(boxes, function(box, k){
33852             
33853             if(!box.length){
33854                 return;
33855             }
33856             
33857             if(box.length == 1){
33858                 queue.push(box);
33859                 return;
33860             }
33861             
33862             filterPattern(box, 4);
33863             
33864         }, this);
33865         
33866         
33867         var prune = [];
33868         
33869         var pos = this.el.getBox(true);
33870         
33871         var minX = pos.x;
33872         
33873         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33874         
33875         var hit_end = false;
33876         
33877         Roo.each(queue, function(box){
33878             
33879             if(hit_end){
33880                 
33881                 Roo.each(box, function(b){
33882                 
33883                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33884                     b.el.hide();
33885
33886                 }, this);
33887
33888                 return;
33889             }
33890             
33891             var mx = 0;
33892             
33893             Roo.each(box, function(b){
33894                 
33895                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33896                 b.el.show();
33897
33898                 mx = Math.max(mx, b.x);
33899                 
33900             }, this);
33901             
33902             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33903             
33904             if(maxX < minX){
33905                 
33906                 Roo.each(box, function(b){
33907                 
33908                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33909                     b.el.hide();
33910                     
33911                 }, this);
33912                 
33913                 hit_end = true;
33914                 
33915                 return;
33916             }
33917             
33918             prune.push(box);
33919             
33920         }, this);
33921         
33922         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33923     },
33924     
33925     /** Sets position of item in DOM
33926     * @param {Element} item
33927     * @param {Number} x - horizontal position
33928     * @param {Number} y - vertical position
33929     * @param {Boolean} isInstant - disables transitions
33930     */
33931     _processVerticalLayoutQueue : function( queue, isInstant )
33932     {
33933         var pos = this.el.getBox(true);
33934         var x = pos.x;
33935         var y = pos.y;
33936         var maxY = [];
33937         
33938         for (var i = 0; i < this.cols; i++){
33939             maxY[i] = pos.y;
33940         }
33941         
33942         Roo.each(queue, function(box, k){
33943             
33944             var col = k % this.cols;
33945             
33946             Roo.each(box, function(b,kk){
33947                 
33948                 b.el.position('absolute');
33949                 
33950                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33951                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33952                 
33953                 if(b.size == 'md-left' || b.size == 'md-right'){
33954                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33955                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33956                 }
33957                 
33958                 b.el.setWidth(width);
33959                 b.el.setHeight(height);
33960                 // iframe?
33961                 b.el.select('iframe',true).setSize(width,height);
33962                 
33963             }, this);
33964             
33965             for (var i = 0; i < this.cols; i++){
33966                 
33967                 if(maxY[i] < maxY[col]){
33968                     col = i;
33969                     continue;
33970                 }
33971                 
33972                 col = Math.min(col, i);
33973                 
33974             }
33975             
33976             x = pos.x + col * (this.colWidth + this.padWidth);
33977             
33978             y = maxY[col];
33979             
33980             var positions = [];
33981             
33982             switch (box.length){
33983                 case 1 :
33984                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33985                     break;
33986                 case 2 :
33987                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33988                     break;
33989                 case 3 :
33990                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33991                     break;
33992                 case 4 :
33993                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33994                     break;
33995                 default :
33996                     break;
33997             }
33998             
33999             Roo.each(box, function(b,kk){
34000                 
34001                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34002                 
34003                 var sz = b.el.getSize();
34004                 
34005                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34006                 
34007             }, this);
34008             
34009         }, this);
34010         
34011         var mY = 0;
34012         
34013         for (var i = 0; i < this.cols; i++){
34014             mY = Math.max(mY, maxY[i]);
34015         }
34016         
34017         this.el.setHeight(mY - pos.y);
34018         
34019     },
34020     
34021 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34022 //    {
34023 //        var pos = this.el.getBox(true);
34024 //        var x = pos.x;
34025 //        var y = pos.y;
34026 //        var maxX = pos.right;
34027 //        
34028 //        var maxHeight = 0;
34029 //        
34030 //        Roo.each(items, function(item, k){
34031 //            
34032 //            var c = k % 2;
34033 //            
34034 //            item.el.position('absolute');
34035 //                
34036 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34037 //
34038 //            item.el.setWidth(width);
34039 //
34040 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34041 //
34042 //            item.el.setHeight(height);
34043 //            
34044 //            if(c == 0){
34045 //                item.el.setXY([x, y], isInstant ? false : true);
34046 //            } else {
34047 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34048 //            }
34049 //            
34050 //            y = y + height + this.alternativePadWidth;
34051 //            
34052 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34053 //            
34054 //        }, this);
34055 //        
34056 //        this.el.setHeight(maxHeight);
34057 //        
34058 //    },
34059     
34060     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34061     {
34062         var pos = this.el.getBox(true);
34063         
34064         var minX = pos.x;
34065         var minY = pos.y;
34066         
34067         var maxX = pos.right;
34068         
34069         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34070         
34071         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34072         
34073         Roo.each(queue, function(box, k){
34074             
34075             Roo.each(box, function(b, kk){
34076                 
34077                 b.el.position('absolute');
34078                 
34079                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34080                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34081                 
34082                 if(b.size == 'md-left' || b.size == 'md-right'){
34083                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34084                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34085                 }
34086                 
34087                 b.el.setWidth(width);
34088                 b.el.setHeight(height);
34089                 
34090             }, this);
34091             
34092             if(!box.length){
34093                 return;
34094             }
34095             
34096             var positions = [];
34097             
34098             switch (box.length){
34099                 case 1 :
34100                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34101                     break;
34102                 case 2 :
34103                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34104                     break;
34105                 case 3 :
34106                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34107                     break;
34108                 case 4 :
34109                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34110                     break;
34111                 default :
34112                     break;
34113             }
34114             
34115             Roo.each(box, function(b,kk){
34116                 
34117                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34118                 
34119                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34120                 
34121             }, this);
34122             
34123         }, this);
34124         
34125     },
34126     
34127     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34128     {
34129         Roo.each(eItems, function(b,k){
34130             
34131             b.size = (k == 0) ? 'sm' : 'xs';
34132             b.x = (k == 0) ? 2 : 1;
34133             b.y = (k == 0) ? 2 : 1;
34134             
34135             b.el.position('absolute');
34136             
34137             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34138                 
34139             b.el.setWidth(width);
34140             
34141             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34142             
34143             b.el.setHeight(height);
34144             
34145         }, this);
34146
34147         var positions = [];
34148         
34149         positions.push({
34150             x : maxX - this.unitWidth * 2 - this.gutter,
34151             y : minY
34152         });
34153         
34154         positions.push({
34155             x : maxX - this.unitWidth,
34156             y : minY + (this.unitWidth + this.gutter) * 2
34157         });
34158         
34159         positions.push({
34160             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34161             y : minY
34162         });
34163         
34164         Roo.each(eItems, function(b,k){
34165             
34166             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34167
34168         }, this);
34169         
34170     },
34171     
34172     getVerticalOneBoxColPositions : function(x, y, box)
34173     {
34174         var pos = [];
34175         
34176         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34177         
34178         if(box[0].size == 'md-left'){
34179             rand = 0;
34180         }
34181         
34182         if(box[0].size == 'md-right'){
34183             rand = 1;
34184         }
34185         
34186         pos.push({
34187             x : x + (this.unitWidth + this.gutter) * rand,
34188             y : y
34189         });
34190         
34191         return pos;
34192     },
34193     
34194     getVerticalTwoBoxColPositions : function(x, y, box)
34195     {
34196         var pos = [];
34197         
34198         if(box[0].size == 'xs'){
34199             
34200             pos.push({
34201                 x : x,
34202                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34203             });
34204
34205             pos.push({
34206                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34207                 y : y
34208             });
34209             
34210             return pos;
34211             
34212         }
34213         
34214         pos.push({
34215             x : x,
34216             y : y
34217         });
34218
34219         pos.push({
34220             x : x + (this.unitWidth + this.gutter) * 2,
34221             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34222         });
34223         
34224         return pos;
34225         
34226     },
34227     
34228     getVerticalThreeBoxColPositions : function(x, y, box)
34229     {
34230         var pos = [];
34231         
34232         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34233             
34234             pos.push({
34235                 x : x,
34236                 y : y
34237             });
34238
34239             pos.push({
34240                 x : x + (this.unitWidth + this.gutter) * 1,
34241                 y : y
34242             });
34243             
34244             pos.push({
34245                 x : x + (this.unitWidth + this.gutter) * 2,
34246                 y : y
34247             });
34248             
34249             return pos;
34250             
34251         }
34252         
34253         if(box[0].size == 'xs' && box[1].size == 'xs'){
34254             
34255             pos.push({
34256                 x : x,
34257                 y : y
34258             });
34259
34260             pos.push({
34261                 x : x,
34262                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34263             });
34264             
34265             pos.push({
34266                 x : x + (this.unitWidth + this.gutter) * 1,
34267                 y : y
34268             });
34269             
34270             return pos;
34271             
34272         }
34273         
34274         pos.push({
34275             x : x,
34276             y : y
34277         });
34278
34279         pos.push({
34280             x : x + (this.unitWidth + this.gutter) * 2,
34281             y : y
34282         });
34283
34284         pos.push({
34285             x : x + (this.unitWidth + this.gutter) * 2,
34286             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34287         });
34288             
34289         return pos;
34290         
34291     },
34292     
34293     getVerticalFourBoxColPositions : function(x, y, box)
34294     {
34295         var pos = [];
34296         
34297         if(box[0].size == 'xs'){
34298             
34299             pos.push({
34300                 x : x,
34301                 y : y
34302             });
34303
34304             pos.push({
34305                 x : x,
34306                 y : y + (this.unitHeight + this.gutter) * 1
34307             });
34308             
34309             pos.push({
34310                 x : x,
34311                 y : y + (this.unitHeight + this.gutter) * 2
34312             });
34313             
34314             pos.push({
34315                 x : x + (this.unitWidth + this.gutter) * 1,
34316                 y : y
34317             });
34318             
34319             return pos;
34320             
34321         }
34322         
34323         pos.push({
34324             x : x,
34325             y : y
34326         });
34327
34328         pos.push({
34329             x : x + (this.unitWidth + this.gutter) * 2,
34330             y : y
34331         });
34332
34333         pos.push({
34334             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34335             y : y + (this.unitHeight + this.gutter) * 1
34336         });
34337
34338         pos.push({
34339             x : x + (this.unitWidth + this.gutter) * 2,
34340             y : y + (this.unitWidth + this.gutter) * 2
34341         });
34342
34343         return pos;
34344         
34345     },
34346     
34347     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34348     {
34349         var pos = [];
34350         
34351         if(box[0].size == 'md-left'){
34352             pos.push({
34353                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34354                 y : minY
34355             });
34356             
34357             return pos;
34358         }
34359         
34360         if(box[0].size == 'md-right'){
34361             pos.push({
34362                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34363                 y : minY + (this.unitWidth + this.gutter) * 1
34364             });
34365             
34366             return pos;
34367         }
34368         
34369         var rand = Math.floor(Math.random() * (4 - box[0].y));
34370         
34371         pos.push({
34372             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34373             y : minY + (this.unitWidth + this.gutter) * rand
34374         });
34375         
34376         return pos;
34377         
34378     },
34379     
34380     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34381     {
34382         var pos = [];
34383         
34384         if(box[0].size == 'xs'){
34385             
34386             pos.push({
34387                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34388                 y : minY
34389             });
34390
34391             pos.push({
34392                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34393                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34394             });
34395             
34396             return pos;
34397             
34398         }
34399         
34400         pos.push({
34401             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34402             y : minY
34403         });
34404
34405         pos.push({
34406             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34407             y : minY + (this.unitWidth + this.gutter) * 2
34408         });
34409         
34410         return pos;
34411         
34412     },
34413     
34414     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34415     {
34416         var pos = [];
34417         
34418         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34419             
34420             pos.push({
34421                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34422                 y : minY
34423             });
34424
34425             pos.push({
34426                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34427                 y : minY + (this.unitWidth + this.gutter) * 1
34428             });
34429             
34430             pos.push({
34431                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34432                 y : minY + (this.unitWidth + this.gutter) * 2
34433             });
34434             
34435             return pos;
34436             
34437         }
34438         
34439         if(box[0].size == 'xs' && box[1].size == 'xs'){
34440             
34441             pos.push({
34442                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34443                 y : minY
34444             });
34445
34446             pos.push({
34447                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34448                 y : minY
34449             });
34450             
34451             pos.push({
34452                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34453                 y : minY + (this.unitWidth + this.gutter) * 1
34454             });
34455             
34456             return pos;
34457             
34458         }
34459         
34460         pos.push({
34461             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34462             y : minY
34463         });
34464
34465         pos.push({
34466             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34467             y : minY + (this.unitWidth + this.gutter) * 2
34468         });
34469
34470         pos.push({
34471             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34472             y : minY + (this.unitWidth + this.gutter) * 2
34473         });
34474             
34475         return pos;
34476         
34477     },
34478     
34479     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34480     {
34481         var pos = [];
34482         
34483         if(box[0].size == 'xs'){
34484             
34485             pos.push({
34486                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34487                 y : minY
34488             });
34489
34490             pos.push({
34491                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34492                 y : minY
34493             });
34494             
34495             pos.push({
34496                 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),
34497                 y : minY
34498             });
34499             
34500             pos.push({
34501                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34502                 y : minY + (this.unitWidth + this.gutter) * 1
34503             });
34504             
34505             return pos;
34506             
34507         }
34508         
34509         pos.push({
34510             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34511             y : minY
34512         });
34513         
34514         pos.push({
34515             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34516             y : minY + (this.unitWidth + this.gutter) * 2
34517         });
34518         
34519         pos.push({
34520             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34521             y : minY + (this.unitWidth + this.gutter) * 2
34522         });
34523         
34524         pos.push({
34525             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),
34526             y : minY + (this.unitWidth + this.gutter) * 2
34527         });
34528
34529         return pos;
34530         
34531     },
34532     
34533     /**
34534     * remove a Masonry Brick
34535     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34536     */
34537     removeBrick : function(brick_id)
34538     {
34539         if (!brick_id) {
34540             return;
34541         }
34542         
34543         for (var i = 0; i<this.bricks.length; i++) {
34544             if (this.bricks[i].id == brick_id) {
34545                 this.bricks.splice(i,1);
34546                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34547                 this.initial();
34548             }
34549         }
34550     },
34551     
34552     /**
34553     * adds a Masonry Brick
34554     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34555     */
34556     addBrick : function(cfg)
34557     {
34558         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34559         //this.register(cn);
34560         cn.parentId = this.id;
34561         cn.render(this.el);
34562         return cn;
34563     },
34564     
34565     /**
34566     * register a Masonry Brick
34567     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34568     */
34569     
34570     register : function(brick)
34571     {
34572         this.bricks.push(brick);
34573         brick.masonryId = this.id;
34574     },
34575     
34576     /**
34577     * clear all the Masonry Brick
34578     */
34579     clearAll : function()
34580     {
34581         this.bricks = [];
34582         //this.getChildContainer().dom.innerHTML = "";
34583         this.el.dom.innerHTML = '';
34584     },
34585     
34586     getSelected : function()
34587     {
34588         if (!this.selectedBrick) {
34589             return false;
34590         }
34591         
34592         return this.selectedBrick;
34593     }
34594 });
34595
34596 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34597     
34598     groups: {},
34599      /**
34600     * register a Masonry Layout
34601     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34602     */
34603     
34604     register : function(layout)
34605     {
34606         this.groups[layout.id] = layout;
34607     },
34608     /**
34609     * fetch a  Masonry Layout based on the masonry layout ID
34610     * @param {string} the masonry layout to add
34611     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34612     */
34613     
34614     get: function(layout_id) {
34615         if (typeof(this.groups[layout_id]) == 'undefined') {
34616             return false;
34617         }
34618         return this.groups[layout_id] ;
34619     }
34620     
34621     
34622     
34623 });
34624
34625  
34626
34627  /**
34628  *
34629  * This is based on 
34630  * http://masonry.desandro.com
34631  *
34632  * The idea is to render all the bricks based on vertical width...
34633  *
34634  * The original code extends 'outlayer' - we might need to use that....
34635  * 
34636  */
34637
34638
34639 /**
34640  * @class Roo.bootstrap.LayoutMasonryAuto
34641  * @extends Roo.bootstrap.Component
34642  * Bootstrap Layout Masonry class
34643  * 
34644  * @constructor
34645  * Create a new Element
34646  * @param {Object} config The config object
34647  */
34648
34649 Roo.bootstrap.LayoutMasonryAuto = function(config){
34650     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34651 };
34652
34653 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34654     
34655       /**
34656      * @cfg {Boolean} isFitWidth  - resize the width..
34657      */   
34658     isFitWidth : false,  // options..
34659     /**
34660      * @cfg {Boolean} isOriginLeft = left align?
34661      */   
34662     isOriginLeft : true,
34663     /**
34664      * @cfg {Boolean} isOriginTop = top align?
34665      */   
34666     isOriginTop : false,
34667     /**
34668      * @cfg {Boolean} isLayoutInstant = no animation?
34669      */   
34670     isLayoutInstant : false, // needed?
34671     /**
34672      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34673      */   
34674     isResizingContainer : true,
34675     /**
34676      * @cfg {Number} columnWidth  width of the columns 
34677      */   
34678     
34679     columnWidth : 0,
34680     
34681     /**
34682      * @cfg {Number} maxCols maximum number of columns
34683      */   
34684     
34685     maxCols: 0,
34686     /**
34687      * @cfg {Number} padHeight padding below box..
34688      */   
34689     
34690     padHeight : 10, 
34691     
34692     /**
34693      * @cfg {Boolean} isAutoInitial defalut true
34694      */   
34695     
34696     isAutoInitial : true, 
34697     
34698     // private?
34699     gutter : 0,
34700     
34701     containerWidth: 0,
34702     initialColumnWidth : 0,
34703     currentSize : null,
34704     
34705     colYs : null, // array.
34706     maxY : 0,
34707     padWidth: 10,
34708     
34709     
34710     tag: 'div',
34711     cls: '',
34712     bricks: null, //CompositeElement
34713     cols : 0, // array?
34714     // element : null, // wrapped now this.el
34715     _isLayoutInited : null, 
34716     
34717     
34718     getAutoCreate : function(){
34719         
34720         var cfg = {
34721             tag: this.tag,
34722             cls: 'blog-masonary-wrapper ' + this.cls,
34723             cn : {
34724                 cls : 'mas-boxes masonary'
34725             }
34726         };
34727         
34728         return cfg;
34729     },
34730     
34731     getChildContainer: function( )
34732     {
34733         if (this.boxesEl) {
34734             return this.boxesEl;
34735         }
34736         
34737         this.boxesEl = this.el.select('.mas-boxes').first();
34738         
34739         return this.boxesEl;
34740     },
34741     
34742     
34743     initEvents : function()
34744     {
34745         var _this = this;
34746         
34747         if(this.isAutoInitial){
34748             Roo.log('hook children rendered');
34749             this.on('childrenrendered', function() {
34750                 Roo.log('children rendered');
34751                 _this.initial();
34752             } ,this);
34753         }
34754         
34755     },
34756     
34757     initial : function()
34758     {
34759         this.reloadItems();
34760
34761         this.currentSize = this.el.getBox(true);
34762
34763         /// was window resize... - let's see if this works..
34764         Roo.EventManager.onWindowResize(this.resize, this); 
34765
34766         if(!this.isAutoInitial){
34767             this.layout();
34768             return;
34769         }
34770         
34771         this.layout.defer(500,this);
34772     },
34773     
34774     reloadItems: function()
34775     {
34776         this.bricks = this.el.select('.masonry-brick', true);
34777         
34778         this.bricks.each(function(b) {
34779             //Roo.log(b.getSize());
34780             if (!b.attr('originalwidth')) {
34781                 b.attr('originalwidth',  b.getSize().width);
34782             }
34783             
34784         });
34785         
34786         Roo.log(this.bricks.elements.length);
34787     },
34788     
34789     resize : function()
34790     {
34791         Roo.log('resize');
34792         var cs = this.el.getBox(true);
34793         
34794         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34795             Roo.log("no change in with or X");
34796             return;
34797         }
34798         this.currentSize = cs;
34799         this.layout();
34800     },
34801     
34802     layout : function()
34803     {
34804          Roo.log('layout');
34805         this._resetLayout();
34806         //this._manageStamps();
34807       
34808         // don't animate first layout
34809         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34810         this.layoutItems( isInstant );
34811       
34812         // flag for initalized
34813         this._isLayoutInited = true;
34814     },
34815     
34816     layoutItems : function( isInstant )
34817     {
34818         //var items = this._getItemsForLayout( this.items );
34819         // original code supports filtering layout items.. we just ignore it..
34820         
34821         this._layoutItems( this.bricks , isInstant );
34822       
34823         this._postLayout();
34824     },
34825     _layoutItems : function ( items , isInstant)
34826     {
34827        //this.fireEvent( 'layout', this, items );
34828     
34829
34830         if ( !items || !items.elements.length ) {
34831           // no items, emit event with empty array
34832             return;
34833         }
34834
34835         var queue = [];
34836         items.each(function(item) {
34837             Roo.log("layout item");
34838             Roo.log(item);
34839             // get x/y object from method
34840             var position = this._getItemLayoutPosition( item );
34841             // enqueue
34842             position.item = item;
34843             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34844             queue.push( position );
34845         }, this);
34846       
34847         this._processLayoutQueue( queue );
34848     },
34849     /** Sets position of item in DOM
34850     * @param {Element} item
34851     * @param {Number} x - horizontal position
34852     * @param {Number} y - vertical position
34853     * @param {Boolean} isInstant - disables transitions
34854     */
34855     _processLayoutQueue : function( queue )
34856     {
34857         for ( var i=0, len = queue.length; i < len; i++ ) {
34858             var obj = queue[i];
34859             obj.item.position('absolute');
34860             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34861         }
34862     },
34863       
34864     
34865     /**
34866     * Any logic you want to do after each layout,
34867     * i.e. size the container
34868     */
34869     _postLayout : function()
34870     {
34871         this.resizeContainer();
34872     },
34873     
34874     resizeContainer : function()
34875     {
34876         if ( !this.isResizingContainer ) {
34877             return;
34878         }
34879         var size = this._getContainerSize();
34880         if ( size ) {
34881             this.el.setSize(size.width,size.height);
34882             this.boxesEl.setSize(size.width,size.height);
34883         }
34884     },
34885     
34886     
34887     
34888     _resetLayout : function()
34889     {
34890         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34891         this.colWidth = this.el.getWidth();
34892         //this.gutter = this.el.getWidth(); 
34893         
34894         this.measureColumns();
34895
34896         // reset column Y
34897         var i = this.cols;
34898         this.colYs = [];
34899         while (i--) {
34900             this.colYs.push( 0 );
34901         }
34902     
34903         this.maxY = 0;
34904     },
34905
34906     measureColumns : function()
34907     {
34908         this.getContainerWidth();
34909       // if columnWidth is 0, default to outerWidth of first item
34910         if ( !this.columnWidth ) {
34911             var firstItem = this.bricks.first();
34912             Roo.log(firstItem);
34913             this.columnWidth  = this.containerWidth;
34914             if (firstItem && firstItem.attr('originalwidth') ) {
34915                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34916             }
34917             // columnWidth fall back to item of first element
34918             Roo.log("set column width?");
34919                         this.initialColumnWidth = this.columnWidth  ;
34920
34921             // if first elem has no width, default to size of container
34922             
34923         }
34924         
34925         
34926         if (this.initialColumnWidth) {
34927             this.columnWidth = this.initialColumnWidth;
34928         }
34929         
34930         
34931             
34932         // column width is fixed at the top - however if container width get's smaller we should
34933         // reduce it...
34934         
34935         // this bit calcs how man columns..
34936             
34937         var columnWidth = this.columnWidth += this.gutter;
34938       
34939         // calculate columns
34940         var containerWidth = this.containerWidth + this.gutter;
34941         
34942         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34943         // fix rounding errors, typically with gutters
34944         var excess = columnWidth - containerWidth % columnWidth;
34945         
34946         
34947         // if overshoot is less than a pixel, round up, otherwise floor it
34948         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34949         cols = Math[ mathMethod ]( cols );
34950         this.cols = Math.max( cols, 1 );
34951         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34952         
34953          // padding positioning..
34954         var totalColWidth = this.cols * this.columnWidth;
34955         var padavail = this.containerWidth - totalColWidth;
34956         // so for 2 columns - we need 3 'pads'
34957         
34958         var padNeeded = (1+this.cols) * this.padWidth;
34959         
34960         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34961         
34962         this.columnWidth += padExtra
34963         //this.padWidth = Math.floor(padavail /  ( this.cols));
34964         
34965         // adjust colum width so that padding is fixed??
34966         
34967         // we have 3 columns ... total = width * 3
34968         // we have X left over... that should be used by 
34969         
34970         //if (this.expandC) {
34971             
34972         //}
34973         
34974         
34975         
34976     },
34977     
34978     getContainerWidth : function()
34979     {
34980        /* // container is parent if fit width
34981         var container = this.isFitWidth ? this.element.parentNode : this.element;
34982         // check that this.size and size are there
34983         // IE8 triggers resize on body size change, so they might not be
34984         
34985         var size = getSize( container );  //FIXME
34986         this.containerWidth = size && size.innerWidth; //FIXME
34987         */
34988          
34989         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34990         
34991     },
34992     
34993     _getItemLayoutPosition : function( item )  // what is item?
34994     {
34995         // we resize the item to our columnWidth..
34996       
34997         item.setWidth(this.columnWidth);
34998         item.autoBoxAdjust  = false;
34999         
35000         var sz = item.getSize();
35001  
35002         // how many columns does this brick span
35003         var remainder = this.containerWidth % this.columnWidth;
35004         
35005         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35006         // round if off by 1 pixel, otherwise use ceil
35007         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35008         colSpan = Math.min( colSpan, this.cols );
35009         
35010         // normally this should be '1' as we dont' currently allow multi width columns..
35011         
35012         var colGroup = this._getColGroup( colSpan );
35013         // get the minimum Y value from the columns
35014         var minimumY = Math.min.apply( Math, colGroup );
35015         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35016         
35017         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35018          
35019         // position the brick
35020         var position = {
35021             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35022             y: this.currentSize.y + minimumY + this.padHeight
35023         };
35024         
35025         Roo.log(position);
35026         // apply setHeight to necessary columns
35027         var setHeight = minimumY + sz.height + this.padHeight;
35028         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35029         
35030         var setSpan = this.cols + 1 - colGroup.length;
35031         for ( var i = 0; i < setSpan; i++ ) {
35032           this.colYs[ shortColIndex + i ] = setHeight ;
35033         }
35034       
35035         return position;
35036     },
35037     
35038     /**
35039      * @param {Number} colSpan - number of columns the element spans
35040      * @returns {Array} colGroup
35041      */
35042     _getColGroup : function( colSpan )
35043     {
35044         if ( colSpan < 2 ) {
35045           // if brick spans only one column, use all the column Ys
35046           return this.colYs;
35047         }
35048       
35049         var colGroup = [];
35050         // how many different places could this brick fit horizontally
35051         var groupCount = this.cols + 1 - colSpan;
35052         // for each group potential horizontal position
35053         for ( var i = 0; i < groupCount; i++ ) {
35054           // make an array of colY values for that one group
35055           var groupColYs = this.colYs.slice( i, i + colSpan );
35056           // and get the max value of the array
35057           colGroup[i] = Math.max.apply( Math, groupColYs );
35058         }
35059         return colGroup;
35060     },
35061     /*
35062     _manageStamp : function( stamp )
35063     {
35064         var stampSize =  stamp.getSize();
35065         var offset = stamp.getBox();
35066         // get the columns that this stamp affects
35067         var firstX = this.isOriginLeft ? offset.x : offset.right;
35068         var lastX = firstX + stampSize.width;
35069         var firstCol = Math.floor( firstX / this.columnWidth );
35070         firstCol = Math.max( 0, firstCol );
35071         
35072         var lastCol = Math.floor( lastX / this.columnWidth );
35073         // lastCol should not go over if multiple of columnWidth #425
35074         lastCol -= lastX % this.columnWidth ? 0 : 1;
35075         lastCol = Math.min( this.cols - 1, lastCol );
35076         
35077         // set colYs to bottom of the stamp
35078         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35079             stampSize.height;
35080             
35081         for ( var i = firstCol; i <= lastCol; i++ ) {
35082           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35083         }
35084     },
35085     */
35086     
35087     _getContainerSize : function()
35088     {
35089         this.maxY = Math.max.apply( Math, this.colYs );
35090         var size = {
35091             height: this.maxY
35092         };
35093       
35094         if ( this.isFitWidth ) {
35095             size.width = this._getContainerFitWidth();
35096         }
35097       
35098         return size;
35099     },
35100     
35101     _getContainerFitWidth : function()
35102     {
35103         var unusedCols = 0;
35104         // count unused columns
35105         var i = this.cols;
35106         while ( --i ) {
35107           if ( this.colYs[i] !== 0 ) {
35108             break;
35109           }
35110           unusedCols++;
35111         }
35112         // fit container to columns that have been used
35113         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35114     },
35115     
35116     needsResizeLayout : function()
35117     {
35118         var previousWidth = this.containerWidth;
35119         this.getContainerWidth();
35120         return previousWidth !== this.containerWidth;
35121     }
35122  
35123 });
35124
35125  
35126
35127  /*
35128  * - LGPL
35129  *
35130  * element
35131  * 
35132  */
35133
35134 /**
35135  * @class Roo.bootstrap.MasonryBrick
35136  * @extends Roo.bootstrap.Component
35137  * Bootstrap MasonryBrick class
35138  * 
35139  * @constructor
35140  * Create a new MasonryBrick
35141  * @param {Object} config The config object
35142  */
35143
35144 Roo.bootstrap.MasonryBrick = function(config){
35145     
35146     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35147     
35148     Roo.bootstrap.MasonryBrick.register(this);
35149     
35150     this.addEvents({
35151         // raw events
35152         /**
35153          * @event click
35154          * When a MasonryBrick is clcik
35155          * @param {Roo.bootstrap.MasonryBrick} this
35156          * @param {Roo.EventObject} e
35157          */
35158         "click" : true
35159     });
35160 };
35161
35162 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35163     
35164     /**
35165      * @cfg {String} title
35166      */   
35167     title : '',
35168     /**
35169      * @cfg {String} html
35170      */   
35171     html : '',
35172     /**
35173      * @cfg {String} bgimage
35174      */   
35175     bgimage : '',
35176     /**
35177      * @cfg {String} videourl
35178      */   
35179     videourl : '',
35180     /**
35181      * @cfg {String} cls
35182      */   
35183     cls : '',
35184     /**
35185      * @cfg {String} href
35186      */   
35187     href : '',
35188     /**
35189      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35190      */   
35191     size : 'xs',
35192     
35193     /**
35194      * @cfg {String} placetitle (center|bottom)
35195      */   
35196     placetitle : '',
35197     
35198     /**
35199      * @cfg {Boolean} isFitContainer defalut true
35200      */   
35201     isFitContainer : true, 
35202     
35203     /**
35204      * @cfg {Boolean} preventDefault defalut false
35205      */   
35206     preventDefault : false, 
35207     
35208     /**
35209      * @cfg {Boolean} inverse defalut false
35210      */   
35211     maskInverse : false, 
35212     
35213     getAutoCreate : function()
35214     {
35215         if(!this.isFitContainer){
35216             return this.getSplitAutoCreate();
35217         }
35218         
35219         var cls = 'masonry-brick masonry-brick-full';
35220         
35221         if(this.href.length){
35222             cls += ' masonry-brick-link';
35223         }
35224         
35225         if(this.bgimage.length){
35226             cls += ' masonry-brick-image';
35227         }
35228         
35229         if(this.maskInverse){
35230             cls += ' mask-inverse';
35231         }
35232         
35233         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35234             cls += ' enable-mask';
35235         }
35236         
35237         if(this.size){
35238             cls += ' masonry-' + this.size + '-brick';
35239         }
35240         
35241         if(this.placetitle.length){
35242             
35243             switch (this.placetitle) {
35244                 case 'center' :
35245                     cls += ' masonry-center-title';
35246                     break;
35247                 case 'bottom' :
35248                     cls += ' masonry-bottom-title';
35249                     break;
35250                 default:
35251                     break;
35252             }
35253             
35254         } else {
35255             if(!this.html.length && !this.bgimage.length){
35256                 cls += ' masonry-center-title';
35257             }
35258
35259             if(!this.html.length && this.bgimage.length){
35260                 cls += ' masonry-bottom-title';
35261             }
35262         }
35263         
35264         if(this.cls){
35265             cls += ' ' + this.cls;
35266         }
35267         
35268         var cfg = {
35269             tag: (this.href.length) ? 'a' : 'div',
35270             cls: cls,
35271             cn: [
35272                 {
35273                     tag: 'div',
35274                     cls: 'masonry-brick-mask'
35275                 },
35276                 {
35277                     tag: 'div',
35278                     cls: 'masonry-brick-paragraph',
35279                     cn: []
35280                 }
35281             ]
35282         };
35283         
35284         if(this.href.length){
35285             cfg.href = this.href;
35286         }
35287         
35288         var cn = cfg.cn[1].cn;
35289         
35290         if(this.title.length){
35291             cn.push({
35292                 tag: 'h4',
35293                 cls: 'masonry-brick-title',
35294                 html: this.title
35295             });
35296         }
35297         
35298         if(this.html.length){
35299             cn.push({
35300                 tag: 'p',
35301                 cls: 'masonry-brick-text',
35302                 html: this.html
35303             });
35304         }
35305         
35306         if (!this.title.length && !this.html.length) {
35307             cfg.cn[1].cls += ' hide';
35308         }
35309         
35310         if(this.bgimage.length){
35311             cfg.cn.push({
35312                 tag: 'img',
35313                 cls: 'masonry-brick-image-view',
35314                 src: this.bgimage
35315             });
35316         }
35317         
35318         if(this.videourl.length){
35319             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35320             // youtube support only?
35321             cfg.cn.push({
35322                 tag: 'iframe',
35323                 cls: 'masonry-brick-image-view',
35324                 src: vurl,
35325                 frameborder : 0,
35326                 allowfullscreen : true
35327             });
35328         }
35329         
35330         return cfg;
35331         
35332     },
35333     
35334     getSplitAutoCreate : function()
35335     {
35336         var cls = 'masonry-brick masonry-brick-split';
35337         
35338         if(this.href.length){
35339             cls += ' masonry-brick-link';
35340         }
35341         
35342         if(this.bgimage.length){
35343             cls += ' masonry-brick-image';
35344         }
35345         
35346         if(this.size){
35347             cls += ' masonry-' + this.size + '-brick';
35348         }
35349         
35350         switch (this.placetitle) {
35351             case 'center' :
35352                 cls += ' masonry-center-title';
35353                 break;
35354             case 'bottom' :
35355                 cls += ' masonry-bottom-title';
35356                 break;
35357             default:
35358                 if(!this.bgimage.length){
35359                     cls += ' masonry-center-title';
35360                 }
35361
35362                 if(this.bgimage.length){
35363                     cls += ' masonry-bottom-title';
35364                 }
35365                 break;
35366         }
35367         
35368         if(this.cls){
35369             cls += ' ' + this.cls;
35370         }
35371         
35372         var cfg = {
35373             tag: (this.href.length) ? 'a' : 'div',
35374             cls: cls,
35375             cn: [
35376                 {
35377                     tag: 'div',
35378                     cls: 'masonry-brick-split-head',
35379                     cn: [
35380                         {
35381                             tag: 'div',
35382                             cls: 'masonry-brick-paragraph',
35383                             cn: []
35384                         }
35385                     ]
35386                 },
35387                 {
35388                     tag: 'div',
35389                     cls: 'masonry-brick-split-body',
35390                     cn: []
35391                 }
35392             ]
35393         };
35394         
35395         if(this.href.length){
35396             cfg.href = this.href;
35397         }
35398         
35399         if(this.title.length){
35400             cfg.cn[0].cn[0].cn.push({
35401                 tag: 'h4',
35402                 cls: 'masonry-brick-title',
35403                 html: this.title
35404             });
35405         }
35406         
35407         if(this.html.length){
35408             cfg.cn[1].cn.push({
35409                 tag: 'p',
35410                 cls: 'masonry-brick-text',
35411                 html: this.html
35412             });
35413         }
35414
35415         if(this.bgimage.length){
35416             cfg.cn[0].cn.push({
35417                 tag: 'img',
35418                 cls: 'masonry-brick-image-view',
35419                 src: this.bgimage
35420             });
35421         }
35422         
35423         if(this.videourl.length){
35424             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35425             // youtube support only?
35426             cfg.cn[0].cn.cn.push({
35427                 tag: 'iframe',
35428                 cls: 'masonry-brick-image-view',
35429                 src: vurl,
35430                 frameborder : 0,
35431                 allowfullscreen : true
35432             });
35433         }
35434         
35435         return cfg;
35436     },
35437     
35438     initEvents: function() 
35439     {
35440         switch (this.size) {
35441             case 'xs' :
35442                 this.x = 1;
35443                 this.y = 1;
35444                 break;
35445             case 'sm' :
35446                 this.x = 2;
35447                 this.y = 2;
35448                 break;
35449             case 'md' :
35450             case 'md-left' :
35451             case 'md-right' :
35452                 this.x = 3;
35453                 this.y = 3;
35454                 break;
35455             case 'tall' :
35456                 this.x = 2;
35457                 this.y = 3;
35458                 break;
35459             case 'wide' :
35460                 this.x = 3;
35461                 this.y = 2;
35462                 break;
35463             case 'wide-thin' :
35464                 this.x = 3;
35465                 this.y = 1;
35466                 break;
35467                         
35468             default :
35469                 break;
35470         }
35471         
35472         if(Roo.isTouch){
35473             this.el.on('touchstart', this.onTouchStart, this);
35474             this.el.on('touchmove', this.onTouchMove, this);
35475             this.el.on('touchend', this.onTouchEnd, this);
35476             this.el.on('contextmenu', this.onContextMenu, this);
35477         } else {
35478             this.el.on('mouseenter'  ,this.enter, this);
35479             this.el.on('mouseleave', this.leave, this);
35480             this.el.on('click', this.onClick, this);
35481         }
35482         
35483         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35484             this.parent().bricks.push(this);   
35485         }
35486         
35487     },
35488     
35489     onClick: function(e, el)
35490     {
35491         var time = this.endTimer - this.startTimer;
35492         // Roo.log(e.preventDefault());
35493         if(Roo.isTouch){
35494             if(time > 1000){
35495                 e.preventDefault();
35496                 return;
35497             }
35498         }
35499         
35500         if(!this.preventDefault){
35501             return;
35502         }
35503         
35504         e.preventDefault();
35505         
35506         if (this.activeClass != '') {
35507             this.selectBrick();
35508         }
35509         
35510         this.fireEvent('click', this, e);
35511     },
35512     
35513     enter: function(e, el)
35514     {
35515         e.preventDefault();
35516         
35517         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35518             return;
35519         }
35520         
35521         if(this.bgimage.length && this.html.length){
35522             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35523         }
35524     },
35525     
35526     leave: function(e, el)
35527     {
35528         e.preventDefault();
35529         
35530         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35531             return;
35532         }
35533         
35534         if(this.bgimage.length && this.html.length){
35535             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35536         }
35537     },
35538     
35539     onTouchStart: function(e, el)
35540     {
35541 //        e.preventDefault();
35542         
35543         this.touchmoved = false;
35544         
35545         if(!this.isFitContainer){
35546             return;
35547         }
35548         
35549         if(!this.bgimage.length || !this.html.length){
35550             return;
35551         }
35552         
35553         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35554         
35555         this.timer = new Date().getTime();
35556         
35557     },
35558     
35559     onTouchMove: function(e, el)
35560     {
35561         this.touchmoved = true;
35562     },
35563     
35564     onContextMenu : function(e,el)
35565     {
35566         e.preventDefault();
35567         e.stopPropagation();
35568         return false;
35569     },
35570     
35571     onTouchEnd: function(e, el)
35572     {
35573 //        e.preventDefault();
35574         
35575         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35576         
35577             this.leave(e,el);
35578             
35579             return;
35580         }
35581         
35582         if(!this.bgimage.length || !this.html.length){
35583             
35584             if(this.href.length){
35585                 window.location.href = this.href;
35586             }
35587             
35588             return;
35589         }
35590         
35591         if(!this.isFitContainer){
35592             return;
35593         }
35594         
35595         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35596         
35597         window.location.href = this.href;
35598     },
35599     
35600     //selection on single brick only
35601     selectBrick : function() {
35602         
35603         if (!this.parentId) {
35604             return;
35605         }
35606         
35607         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35608         var index = m.selectedBrick.indexOf(this.id);
35609         
35610         if ( index > -1) {
35611             m.selectedBrick.splice(index,1);
35612             this.el.removeClass(this.activeClass);
35613             return;
35614         }
35615         
35616         for(var i = 0; i < m.selectedBrick.length; i++) {
35617             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35618             b.el.removeClass(b.activeClass);
35619         }
35620         
35621         m.selectedBrick = [];
35622         
35623         m.selectedBrick.push(this.id);
35624         this.el.addClass(this.activeClass);
35625         return;
35626     },
35627     
35628     isSelected : function(){
35629         return this.el.hasClass(this.activeClass);
35630         
35631     }
35632 });
35633
35634 Roo.apply(Roo.bootstrap.MasonryBrick, {
35635     
35636     //groups: {},
35637     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35638      /**
35639     * register a Masonry Brick
35640     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35641     */
35642     
35643     register : function(brick)
35644     {
35645         //this.groups[brick.id] = brick;
35646         this.groups.add(brick.id, brick);
35647     },
35648     /**
35649     * fetch a  masonry brick based on the masonry brick ID
35650     * @param {string} the masonry brick to add
35651     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35652     */
35653     
35654     get: function(brick_id) 
35655     {
35656         // if (typeof(this.groups[brick_id]) == 'undefined') {
35657         //     return false;
35658         // }
35659         // return this.groups[brick_id] ;
35660         
35661         if(this.groups.key(brick_id)) {
35662             return this.groups.key(brick_id);
35663         }
35664         
35665         return false;
35666     }
35667     
35668     
35669     
35670 });
35671
35672  /*
35673  * - LGPL
35674  *
35675  * element
35676  * 
35677  */
35678
35679 /**
35680  * @class Roo.bootstrap.Brick
35681  * @extends Roo.bootstrap.Component
35682  * Bootstrap Brick class
35683  * 
35684  * @constructor
35685  * Create a new Brick
35686  * @param {Object} config The config object
35687  */
35688
35689 Roo.bootstrap.Brick = function(config){
35690     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35691     
35692     this.addEvents({
35693         // raw events
35694         /**
35695          * @event click
35696          * When a Brick is click
35697          * @param {Roo.bootstrap.Brick} this
35698          * @param {Roo.EventObject} e
35699          */
35700         "click" : true
35701     });
35702 };
35703
35704 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35705     
35706     /**
35707      * @cfg {String} title
35708      */   
35709     title : '',
35710     /**
35711      * @cfg {String} html
35712      */   
35713     html : '',
35714     /**
35715      * @cfg {String} bgimage
35716      */   
35717     bgimage : '',
35718     /**
35719      * @cfg {String} cls
35720      */   
35721     cls : '',
35722     /**
35723      * @cfg {String} href
35724      */   
35725     href : '',
35726     /**
35727      * @cfg {String} video
35728      */   
35729     video : '',
35730     /**
35731      * @cfg {Boolean} square
35732      */   
35733     square : true,
35734     
35735     getAutoCreate : function()
35736     {
35737         var cls = 'roo-brick';
35738         
35739         if(this.href.length){
35740             cls += ' roo-brick-link';
35741         }
35742         
35743         if(this.bgimage.length){
35744             cls += ' roo-brick-image';
35745         }
35746         
35747         if(!this.html.length && !this.bgimage.length){
35748             cls += ' roo-brick-center-title';
35749         }
35750         
35751         if(!this.html.length && this.bgimage.length){
35752             cls += ' roo-brick-bottom-title';
35753         }
35754         
35755         if(this.cls){
35756             cls += ' ' + this.cls;
35757         }
35758         
35759         var cfg = {
35760             tag: (this.href.length) ? 'a' : 'div',
35761             cls: cls,
35762             cn: [
35763                 {
35764                     tag: 'div',
35765                     cls: 'roo-brick-paragraph',
35766                     cn: []
35767                 }
35768             ]
35769         };
35770         
35771         if(this.href.length){
35772             cfg.href = this.href;
35773         }
35774         
35775         var cn = cfg.cn[0].cn;
35776         
35777         if(this.title.length){
35778             cn.push({
35779                 tag: 'h4',
35780                 cls: 'roo-brick-title',
35781                 html: this.title
35782             });
35783         }
35784         
35785         if(this.html.length){
35786             cn.push({
35787                 tag: 'p',
35788                 cls: 'roo-brick-text',
35789                 html: this.html
35790             });
35791         } else {
35792             cn.cls += ' hide';
35793         }
35794         
35795         if(this.bgimage.length){
35796             cfg.cn.push({
35797                 tag: 'img',
35798                 cls: 'roo-brick-image-view',
35799                 src: this.bgimage
35800             });
35801         }
35802         
35803         return cfg;
35804     },
35805     
35806     initEvents: function() 
35807     {
35808         if(this.title.length || this.html.length){
35809             this.el.on('mouseenter'  ,this.enter, this);
35810             this.el.on('mouseleave', this.leave, this);
35811         }
35812         
35813         Roo.EventManager.onWindowResize(this.resize, this); 
35814         
35815         if(this.bgimage.length){
35816             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35817             this.imageEl.on('load', this.onImageLoad, this);
35818             return;
35819         }
35820         
35821         this.resize();
35822     },
35823     
35824     onImageLoad : function()
35825     {
35826         this.resize();
35827     },
35828     
35829     resize : function()
35830     {
35831         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35832         
35833         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35834         
35835         if(this.bgimage.length){
35836             var image = this.el.select('.roo-brick-image-view', true).first();
35837             
35838             image.setWidth(paragraph.getWidth());
35839             
35840             if(this.square){
35841                 image.setHeight(paragraph.getWidth());
35842             }
35843             
35844             this.el.setHeight(image.getHeight());
35845             paragraph.setHeight(image.getHeight());
35846             
35847         }
35848         
35849     },
35850     
35851     enter: function(e, el)
35852     {
35853         e.preventDefault();
35854         
35855         if(this.bgimage.length){
35856             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35857             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35858         }
35859     },
35860     
35861     leave: function(e, el)
35862     {
35863         e.preventDefault();
35864         
35865         if(this.bgimage.length){
35866             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35867             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35868         }
35869     }
35870     
35871 });
35872
35873  
35874
35875  /*
35876  * - LGPL
35877  *
35878  * Number field 
35879  */
35880
35881 /**
35882  * @class Roo.bootstrap.NumberField
35883  * @extends Roo.bootstrap.Input
35884  * Bootstrap NumberField class
35885  * 
35886  * 
35887  * 
35888  * 
35889  * @constructor
35890  * Create a new NumberField
35891  * @param {Object} config The config object
35892  */
35893
35894 Roo.bootstrap.NumberField = function(config){
35895     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35896 };
35897
35898 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35899     
35900     /**
35901      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35902      */
35903     allowDecimals : true,
35904     /**
35905      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35906      */
35907     decimalSeparator : ".",
35908     /**
35909      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35910      */
35911     decimalPrecision : 2,
35912     /**
35913      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35914      */
35915     allowNegative : true,
35916     
35917     /**
35918      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35919      */
35920     allowZero: true,
35921     /**
35922      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35923      */
35924     minValue : Number.NEGATIVE_INFINITY,
35925     /**
35926      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35927      */
35928     maxValue : Number.MAX_VALUE,
35929     /**
35930      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35931      */
35932     minText : "The minimum value for this field is {0}",
35933     /**
35934      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35935      */
35936     maxText : "The maximum value for this field is {0}",
35937     /**
35938      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35939      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35940      */
35941     nanText : "{0} is not a valid number",
35942     /**
35943      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35944      */
35945     thousandsDelimiter : false,
35946     /**
35947      * @cfg {String} valueAlign alignment of value
35948      */
35949     valueAlign : "left",
35950
35951     getAutoCreate : function()
35952     {
35953         var hiddenInput = {
35954             tag: 'input',
35955             type: 'hidden',
35956             id: Roo.id(),
35957             cls: 'hidden-number-input'
35958         };
35959         
35960         if (this.name) {
35961             hiddenInput.name = this.name;
35962         }
35963         
35964         this.name = '';
35965         
35966         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35967         
35968         this.name = hiddenInput.name;
35969         
35970         if(cfg.cn.length > 0) {
35971             cfg.cn.push(hiddenInput);
35972         }
35973         
35974         return cfg;
35975     },
35976
35977     // private
35978     initEvents : function()
35979     {   
35980         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35981         
35982         var allowed = "0123456789";
35983         
35984         if(this.allowDecimals){
35985             allowed += this.decimalSeparator;
35986         }
35987         
35988         if(this.allowNegative){
35989             allowed += "-";
35990         }
35991         
35992         if(this.thousandsDelimiter) {
35993             allowed += ",";
35994         }
35995         
35996         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35997         
35998         var keyPress = function(e){
35999             
36000             var k = e.getKey();
36001             
36002             var c = e.getCharCode();
36003             
36004             if(
36005                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36006                     allowed.indexOf(String.fromCharCode(c)) === -1
36007             ){
36008                 e.stopEvent();
36009                 return;
36010             }
36011             
36012             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36013                 return;
36014             }
36015             
36016             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36017                 e.stopEvent();
36018             }
36019         };
36020         
36021         this.el.on("keypress", keyPress, this);
36022     },
36023     
36024     validateValue : function(value)
36025     {
36026         
36027         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36028             return false;
36029         }
36030         
36031         var num = this.parseValue(value);
36032         
36033         if(isNaN(num)){
36034             this.markInvalid(String.format(this.nanText, value));
36035             return false;
36036         }
36037         
36038         if(num < this.minValue){
36039             this.markInvalid(String.format(this.minText, this.minValue));
36040             return false;
36041         }
36042         
36043         if(num > this.maxValue){
36044             this.markInvalid(String.format(this.maxText, this.maxValue));
36045             return false;
36046         }
36047         
36048         return true;
36049     },
36050
36051     getValue : function()
36052     {
36053         var v = this.hiddenEl().getValue();
36054         
36055         return this.fixPrecision(this.parseValue(v));
36056     },
36057
36058     parseValue : function(value)
36059     {
36060         if(this.thousandsDelimiter) {
36061             value += "";
36062             r = new RegExp(",", "g");
36063             value = value.replace(r, "");
36064         }
36065         
36066         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36067         return isNaN(value) ? '' : value;
36068     },
36069
36070     fixPrecision : function(value)
36071     {
36072         if(this.thousandsDelimiter) {
36073             value += "";
36074             r = new RegExp(",", "g");
36075             value = value.replace(r, "");
36076         }
36077         
36078         var nan = isNaN(value);
36079         
36080         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36081             return nan ? '' : value;
36082         }
36083         return parseFloat(value).toFixed(this.decimalPrecision);
36084     },
36085
36086     setValue : function(v)
36087     {
36088         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36089         
36090         this.value = v;
36091         
36092         if(this.rendered){
36093             
36094             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36095             
36096             this.inputEl().dom.value = (v == '') ? '' :
36097                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36098             
36099             if(!this.allowZero && v === '0') {
36100                 this.hiddenEl().dom.value = '';
36101                 this.inputEl().dom.value = '';
36102             }
36103             
36104             this.validate();
36105         }
36106     },
36107
36108     decimalPrecisionFcn : function(v)
36109     {
36110         return Math.floor(v);
36111     },
36112
36113     beforeBlur : function()
36114     {
36115         var v = this.parseValue(this.getRawValue());
36116         
36117         if(v || v === 0 || v === ''){
36118             this.setValue(v);
36119         }
36120     },
36121     
36122     hiddenEl : function()
36123     {
36124         return this.el.select('input.hidden-number-input',true).first();
36125     }
36126     
36127 });
36128
36129  
36130
36131 /*
36132 * Licence: LGPL
36133 */
36134
36135 /**
36136  * @class Roo.bootstrap.DocumentSlider
36137  * @extends Roo.bootstrap.Component
36138  * Bootstrap DocumentSlider class
36139  * 
36140  * @constructor
36141  * Create a new DocumentViewer
36142  * @param {Object} config The config object
36143  */
36144
36145 Roo.bootstrap.DocumentSlider = function(config){
36146     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36147     
36148     this.files = [];
36149     
36150     this.addEvents({
36151         /**
36152          * @event initial
36153          * Fire after initEvent
36154          * @param {Roo.bootstrap.DocumentSlider} this
36155          */
36156         "initial" : true,
36157         /**
36158          * @event update
36159          * Fire after update
36160          * @param {Roo.bootstrap.DocumentSlider} this
36161          */
36162         "update" : true,
36163         /**
36164          * @event click
36165          * Fire after click
36166          * @param {Roo.bootstrap.DocumentSlider} this
36167          */
36168         "click" : true
36169     });
36170 };
36171
36172 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36173     
36174     files : false,
36175     
36176     indicator : 0,
36177     
36178     getAutoCreate : function()
36179     {
36180         var cfg = {
36181             tag : 'div',
36182             cls : 'roo-document-slider',
36183             cn : [
36184                 {
36185                     tag : 'div',
36186                     cls : 'roo-document-slider-header',
36187                     cn : [
36188                         {
36189                             tag : 'div',
36190                             cls : 'roo-document-slider-header-title'
36191                         }
36192                     ]
36193                 },
36194                 {
36195                     tag : 'div',
36196                     cls : 'roo-document-slider-body',
36197                     cn : [
36198                         {
36199                             tag : 'div',
36200                             cls : 'roo-document-slider-prev',
36201                             cn : [
36202                                 {
36203                                     tag : 'i',
36204                                     cls : 'fa fa-chevron-left'
36205                                 }
36206                             ]
36207                         },
36208                         {
36209                             tag : 'div',
36210                             cls : 'roo-document-slider-thumb',
36211                             cn : [
36212                                 {
36213                                     tag : 'img',
36214                                     cls : 'roo-document-slider-image'
36215                                 }
36216                             ]
36217                         },
36218                         {
36219                             tag : 'div',
36220                             cls : 'roo-document-slider-next',
36221                             cn : [
36222                                 {
36223                                     tag : 'i',
36224                                     cls : 'fa fa-chevron-right'
36225                                 }
36226                             ]
36227                         }
36228                     ]
36229                 }
36230             ]
36231         };
36232         
36233         return cfg;
36234     },
36235     
36236     initEvents : function()
36237     {
36238         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36239         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36240         
36241         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36242         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36243         
36244         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36245         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36246         
36247         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36248         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36249         
36250         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36251         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36252         
36253         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36254         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36255         
36256         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36257         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36258         
36259         this.thumbEl.on('click', this.onClick, this);
36260         
36261         this.prevIndicator.on('click', this.prev, this);
36262         
36263         this.nextIndicator.on('click', this.next, this);
36264         
36265     },
36266     
36267     initial : function()
36268     {
36269         if(this.files.length){
36270             this.indicator = 1;
36271             this.update()
36272         }
36273         
36274         this.fireEvent('initial', this);
36275     },
36276     
36277     update : function()
36278     {
36279         this.imageEl.attr('src', this.files[this.indicator - 1]);
36280         
36281         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36282         
36283         this.prevIndicator.show();
36284         
36285         if(this.indicator == 1){
36286             this.prevIndicator.hide();
36287         }
36288         
36289         this.nextIndicator.show();
36290         
36291         if(this.indicator == this.files.length){
36292             this.nextIndicator.hide();
36293         }
36294         
36295         this.thumbEl.scrollTo('top');
36296         
36297         this.fireEvent('update', this);
36298     },
36299     
36300     onClick : function(e)
36301     {
36302         e.preventDefault();
36303         
36304         this.fireEvent('click', this);
36305     },
36306     
36307     prev : function(e)
36308     {
36309         e.preventDefault();
36310         
36311         this.indicator = Math.max(1, this.indicator - 1);
36312         
36313         this.update();
36314     },
36315     
36316     next : function(e)
36317     {
36318         e.preventDefault();
36319         
36320         this.indicator = Math.min(this.files.length, this.indicator + 1);
36321         
36322         this.update();
36323     }
36324 });
36325 /*
36326  * - LGPL
36327  *
36328  * RadioSet
36329  *
36330  *
36331  */
36332
36333 /**
36334  * @class Roo.bootstrap.RadioSet
36335  * @extends Roo.bootstrap.Input
36336  * Bootstrap RadioSet class
36337  * @cfg {String} indicatorpos (left|right) default left
36338  * @cfg {Boolean} inline (true|false) inline the element (default true)
36339  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36340  * @constructor
36341  * Create a new RadioSet
36342  * @param {Object} config The config object
36343  */
36344
36345 Roo.bootstrap.RadioSet = function(config){
36346     
36347     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36348     
36349     this.radioes = [];
36350     
36351     Roo.bootstrap.RadioSet.register(this);
36352     
36353     this.addEvents({
36354         /**
36355         * @event check
36356         * Fires when the element is checked or unchecked.
36357         * @param {Roo.bootstrap.RadioSet} this This radio
36358         * @param {Roo.bootstrap.Radio} item The checked item
36359         */
36360        check : true,
36361        /**
36362         * @event click
36363         * Fires when the element is click.
36364         * @param {Roo.bootstrap.RadioSet} this This radio set
36365         * @param {Roo.bootstrap.Radio} item The checked item
36366         * @param {Roo.EventObject} e The event object
36367         */
36368        click : true
36369     });
36370     
36371 };
36372
36373 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36374
36375     radioes : false,
36376     
36377     inline : true,
36378     
36379     weight : '',
36380     
36381     indicatorpos : 'left',
36382     
36383     getAutoCreate : function()
36384     {
36385         var label = {
36386             tag : 'label',
36387             cls : 'roo-radio-set-label',
36388             cn : [
36389                 {
36390                     tag : 'span',
36391                     html : this.fieldLabel
36392                 }
36393             ]
36394         };
36395         if (Roo.bootstrap.version == 3) {
36396             
36397             
36398             if(this.indicatorpos == 'left'){
36399                 label.cn.unshift({
36400                     tag : 'i',
36401                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36402                     tooltip : 'This field is required'
36403                 });
36404             } else {
36405                 label.cn.push({
36406                     tag : 'i',
36407                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36408                     tooltip : 'This field is required'
36409                 });
36410             }
36411         }
36412         var items = {
36413             tag : 'div',
36414             cls : 'roo-radio-set-items'
36415         };
36416         
36417         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36418         
36419         if (align === 'left' && this.fieldLabel.length) {
36420             
36421             items = {
36422                 cls : "roo-radio-set-right", 
36423                 cn: [
36424                     items
36425                 ]
36426             };
36427             
36428             if(this.labelWidth > 12){
36429                 label.style = "width: " + this.labelWidth + 'px';
36430             }
36431             
36432             if(this.labelWidth < 13 && this.labelmd == 0){
36433                 this.labelmd = this.labelWidth;
36434             }
36435             
36436             if(this.labellg > 0){
36437                 label.cls += ' col-lg-' + this.labellg;
36438                 items.cls += ' col-lg-' + (12 - this.labellg);
36439             }
36440             
36441             if(this.labelmd > 0){
36442                 label.cls += ' col-md-' + this.labelmd;
36443                 items.cls += ' col-md-' + (12 - this.labelmd);
36444             }
36445             
36446             if(this.labelsm > 0){
36447                 label.cls += ' col-sm-' + this.labelsm;
36448                 items.cls += ' col-sm-' + (12 - this.labelsm);
36449             }
36450             
36451             if(this.labelxs > 0){
36452                 label.cls += ' col-xs-' + this.labelxs;
36453                 items.cls += ' col-xs-' + (12 - this.labelxs);
36454             }
36455         }
36456         
36457         var cfg = {
36458             tag : 'div',
36459             cls : 'roo-radio-set',
36460             cn : [
36461                 {
36462                     tag : 'input',
36463                     cls : 'roo-radio-set-input',
36464                     type : 'hidden',
36465                     name : this.name,
36466                     value : this.value ? this.value :  ''
36467                 },
36468                 label,
36469                 items
36470             ]
36471         };
36472         
36473         if(this.weight.length){
36474             cfg.cls += ' roo-radio-' + this.weight;
36475         }
36476         
36477         if(this.inline) {
36478             cfg.cls += ' roo-radio-set-inline';
36479         }
36480         
36481         var settings=this;
36482         ['xs','sm','md','lg'].map(function(size){
36483             if (settings[size]) {
36484                 cfg.cls += ' col-' + size + '-' + settings[size];
36485             }
36486         });
36487         
36488         return cfg;
36489         
36490     },
36491
36492     initEvents : function()
36493     {
36494         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36495         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36496         
36497         if(!this.fieldLabel.length){
36498             this.labelEl.hide();
36499         }
36500         
36501         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36502         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36503         
36504         this.indicator = this.indicatorEl();
36505         
36506         if(this.indicator){
36507             this.indicator.addClass('invisible');
36508         }
36509         
36510         this.originalValue = this.getValue();
36511         
36512     },
36513     
36514     inputEl: function ()
36515     {
36516         return this.el.select('.roo-radio-set-input', true).first();
36517     },
36518     
36519     getChildContainer : function()
36520     {
36521         return this.itemsEl;
36522     },
36523     
36524     register : function(item)
36525     {
36526         this.radioes.push(item);
36527         
36528     },
36529     
36530     validate : function()
36531     {   
36532         if(this.getVisibilityEl().hasClass('hidden')){
36533             return true;
36534         }
36535         
36536         var valid = false;
36537         
36538         Roo.each(this.radioes, function(i){
36539             if(!i.checked){
36540                 return;
36541             }
36542             
36543             valid = true;
36544             return false;
36545         });
36546         
36547         if(this.allowBlank) {
36548             return true;
36549         }
36550         
36551         if(this.disabled || valid){
36552             this.markValid();
36553             return true;
36554         }
36555         
36556         this.markInvalid();
36557         return false;
36558         
36559     },
36560     
36561     markValid : function()
36562     {
36563         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36564             this.indicatorEl().removeClass('visible');
36565             this.indicatorEl().addClass('invisible');
36566         }
36567         
36568         
36569         if (Roo.bootstrap.version == 3) {
36570             this.el.removeClass([this.invalidClass, this.validClass]);
36571             this.el.addClass(this.validClass);
36572         } else {
36573             this.el.removeClass(['is-invalid','is-valid']);
36574             this.el.addClass(['is-valid']);
36575         }
36576         this.fireEvent('valid', this);
36577     },
36578     
36579     markInvalid : function(msg)
36580     {
36581         if(this.allowBlank || this.disabled){
36582             return;
36583         }
36584         
36585         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36586             this.indicatorEl().removeClass('invisible');
36587             this.indicatorEl().addClass('visible');
36588         }
36589         if (Roo.bootstrap.version == 3) {
36590             this.el.removeClass([this.invalidClass, this.validClass]);
36591             this.el.addClass(this.invalidClass);
36592         } else {
36593             this.el.removeClass(['is-invalid','is-valid']);
36594             this.el.addClass(['is-invalid']);
36595         }
36596         
36597         this.fireEvent('invalid', this, msg);
36598         
36599     },
36600     
36601     setValue : function(v, suppressEvent)
36602     {   
36603         if(this.value === v){
36604             return;
36605         }
36606         
36607         this.value = v;
36608         
36609         if(this.rendered){
36610             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36611         }
36612         
36613         Roo.each(this.radioes, function(i){
36614             i.checked = false;
36615             i.el.removeClass('checked');
36616         });
36617         
36618         Roo.each(this.radioes, function(i){
36619             
36620             if(i.value === v || i.value.toString() === v.toString()){
36621                 i.checked = true;
36622                 i.el.addClass('checked');
36623                 
36624                 if(suppressEvent !== true){
36625                     this.fireEvent('check', this, i);
36626                 }
36627                 
36628                 return false;
36629             }
36630             
36631         }, this);
36632         
36633         this.validate();
36634     },
36635     
36636     clearInvalid : function(){
36637         
36638         if(!this.el || this.preventMark){
36639             return;
36640         }
36641         
36642         this.el.removeClass([this.invalidClass]);
36643         
36644         this.fireEvent('valid', this);
36645     }
36646     
36647 });
36648
36649 Roo.apply(Roo.bootstrap.RadioSet, {
36650     
36651     groups: {},
36652     
36653     register : function(set)
36654     {
36655         this.groups[set.name] = set;
36656     },
36657     
36658     get: function(name) 
36659     {
36660         if (typeof(this.groups[name]) == 'undefined') {
36661             return false;
36662         }
36663         
36664         return this.groups[name] ;
36665     }
36666     
36667 });
36668 /*
36669  * Based on:
36670  * Ext JS Library 1.1.1
36671  * Copyright(c) 2006-2007, Ext JS, LLC.
36672  *
36673  * Originally Released Under LGPL - original licence link has changed is not relivant.
36674  *
36675  * Fork - LGPL
36676  * <script type="text/javascript">
36677  */
36678
36679
36680 /**
36681  * @class Roo.bootstrap.SplitBar
36682  * @extends Roo.util.Observable
36683  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36684  * <br><br>
36685  * Usage:
36686  * <pre><code>
36687 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36688                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36689 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36690 split.minSize = 100;
36691 split.maxSize = 600;
36692 split.animate = true;
36693 split.on('moved', splitterMoved);
36694 </code></pre>
36695  * @constructor
36696  * Create a new SplitBar
36697  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36698  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36699  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36700  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36701                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36702                         position of the SplitBar).
36703  */
36704 Roo.bootstrap.SplitBar = function(cfg){
36705     
36706     /** @private */
36707     
36708     //{
36709     //  dragElement : elm
36710     //  resizingElement: el,
36711         // optional..
36712     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36713     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36714         // existingProxy ???
36715     //}
36716     
36717     this.el = Roo.get(cfg.dragElement, true);
36718     this.el.dom.unselectable = "on";
36719     /** @private */
36720     this.resizingEl = Roo.get(cfg.resizingElement, true);
36721
36722     /**
36723      * @private
36724      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36725      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36726      * @type Number
36727      */
36728     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36729     
36730     /**
36731      * The minimum size of the resizing element. (Defaults to 0)
36732      * @type Number
36733      */
36734     this.minSize = 0;
36735     
36736     /**
36737      * The maximum size of the resizing element. (Defaults to 2000)
36738      * @type Number
36739      */
36740     this.maxSize = 2000;
36741     
36742     /**
36743      * Whether to animate the transition to the new size
36744      * @type Boolean
36745      */
36746     this.animate = false;
36747     
36748     /**
36749      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36750      * @type Boolean
36751      */
36752     this.useShim = false;
36753     
36754     /** @private */
36755     this.shim = null;
36756     
36757     if(!cfg.existingProxy){
36758         /** @private */
36759         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36760     }else{
36761         this.proxy = Roo.get(cfg.existingProxy).dom;
36762     }
36763     /** @private */
36764     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36765     
36766     /** @private */
36767     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36768     
36769     /** @private */
36770     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36771     
36772     /** @private */
36773     this.dragSpecs = {};
36774     
36775     /**
36776      * @private The adapter to use to positon and resize elements
36777      */
36778     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36779     this.adapter.init(this);
36780     
36781     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36782         /** @private */
36783         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36784         this.el.addClass("roo-splitbar-h");
36785     }else{
36786         /** @private */
36787         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36788         this.el.addClass("roo-splitbar-v");
36789     }
36790     
36791     this.addEvents({
36792         /**
36793          * @event resize
36794          * Fires when the splitter is moved (alias for {@link #event-moved})
36795          * @param {Roo.bootstrap.SplitBar} this
36796          * @param {Number} newSize the new width or height
36797          */
36798         "resize" : true,
36799         /**
36800          * @event moved
36801          * Fires when the splitter is moved
36802          * @param {Roo.bootstrap.SplitBar} this
36803          * @param {Number} newSize the new width or height
36804          */
36805         "moved" : true,
36806         /**
36807          * @event beforeresize
36808          * Fires before the splitter is dragged
36809          * @param {Roo.bootstrap.SplitBar} this
36810          */
36811         "beforeresize" : true,
36812
36813         "beforeapply" : true
36814     });
36815
36816     Roo.util.Observable.call(this);
36817 };
36818
36819 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36820     onStartProxyDrag : function(x, y){
36821         this.fireEvent("beforeresize", this);
36822         if(!this.overlay){
36823             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36824             o.unselectable();
36825             o.enableDisplayMode("block");
36826             // all splitbars share the same overlay
36827             Roo.bootstrap.SplitBar.prototype.overlay = o;
36828         }
36829         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36830         this.overlay.show();
36831         Roo.get(this.proxy).setDisplayed("block");
36832         var size = this.adapter.getElementSize(this);
36833         this.activeMinSize = this.getMinimumSize();;
36834         this.activeMaxSize = this.getMaximumSize();;
36835         var c1 = size - this.activeMinSize;
36836         var c2 = Math.max(this.activeMaxSize - size, 0);
36837         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36838             this.dd.resetConstraints();
36839             this.dd.setXConstraint(
36840                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36841                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36842             );
36843             this.dd.setYConstraint(0, 0);
36844         }else{
36845             this.dd.resetConstraints();
36846             this.dd.setXConstraint(0, 0);
36847             this.dd.setYConstraint(
36848                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36849                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36850             );
36851          }
36852         this.dragSpecs.startSize = size;
36853         this.dragSpecs.startPoint = [x, y];
36854         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36855     },
36856     
36857     /** 
36858      * @private Called after the drag operation by the DDProxy
36859      */
36860     onEndProxyDrag : function(e){
36861         Roo.get(this.proxy).setDisplayed(false);
36862         var endPoint = Roo.lib.Event.getXY(e);
36863         if(this.overlay){
36864             this.overlay.hide();
36865         }
36866         var newSize;
36867         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36868             newSize = this.dragSpecs.startSize + 
36869                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36870                     endPoint[0] - this.dragSpecs.startPoint[0] :
36871                     this.dragSpecs.startPoint[0] - endPoint[0]
36872                 );
36873         }else{
36874             newSize = this.dragSpecs.startSize + 
36875                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36876                     endPoint[1] - this.dragSpecs.startPoint[1] :
36877                     this.dragSpecs.startPoint[1] - endPoint[1]
36878                 );
36879         }
36880         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36881         if(newSize != this.dragSpecs.startSize){
36882             if(this.fireEvent('beforeapply', this, newSize) !== false){
36883                 this.adapter.setElementSize(this, newSize);
36884                 this.fireEvent("moved", this, newSize);
36885                 this.fireEvent("resize", this, newSize);
36886             }
36887         }
36888     },
36889     
36890     /**
36891      * Get the adapter this SplitBar uses
36892      * @return The adapter object
36893      */
36894     getAdapter : function(){
36895         return this.adapter;
36896     },
36897     
36898     /**
36899      * Set the adapter this SplitBar uses
36900      * @param {Object} adapter A SplitBar adapter object
36901      */
36902     setAdapter : function(adapter){
36903         this.adapter = adapter;
36904         this.adapter.init(this);
36905     },
36906     
36907     /**
36908      * Gets the minimum size for the resizing element
36909      * @return {Number} The minimum size
36910      */
36911     getMinimumSize : function(){
36912         return this.minSize;
36913     },
36914     
36915     /**
36916      * Sets the minimum size for the resizing element
36917      * @param {Number} minSize The minimum size
36918      */
36919     setMinimumSize : function(minSize){
36920         this.minSize = minSize;
36921     },
36922     
36923     /**
36924      * Gets the maximum size for the resizing element
36925      * @return {Number} The maximum size
36926      */
36927     getMaximumSize : function(){
36928         return this.maxSize;
36929     },
36930     
36931     /**
36932      * Sets the maximum size for the resizing element
36933      * @param {Number} maxSize The maximum size
36934      */
36935     setMaximumSize : function(maxSize){
36936         this.maxSize = maxSize;
36937     },
36938     
36939     /**
36940      * Sets the initialize size for the resizing element
36941      * @param {Number} size The initial size
36942      */
36943     setCurrentSize : function(size){
36944         var oldAnimate = this.animate;
36945         this.animate = false;
36946         this.adapter.setElementSize(this, size);
36947         this.animate = oldAnimate;
36948     },
36949     
36950     /**
36951      * Destroy this splitbar. 
36952      * @param {Boolean} removeEl True to remove the element
36953      */
36954     destroy : function(removeEl){
36955         if(this.shim){
36956             this.shim.remove();
36957         }
36958         this.dd.unreg();
36959         this.proxy.parentNode.removeChild(this.proxy);
36960         if(removeEl){
36961             this.el.remove();
36962         }
36963     }
36964 });
36965
36966 /**
36967  * @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.
36968  */
36969 Roo.bootstrap.SplitBar.createProxy = function(dir){
36970     var proxy = new Roo.Element(document.createElement("div"));
36971     proxy.unselectable();
36972     var cls = 'roo-splitbar-proxy';
36973     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36974     document.body.appendChild(proxy.dom);
36975     return proxy.dom;
36976 };
36977
36978 /** 
36979  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36980  * Default Adapter. It assumes the splitter and resizing element are not positioned
36981  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36982  */
36983 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36984 };
36985
36986 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36987     // do nothing for now
36988     init : function(s){
36989     
36990     },
36991     /**
36992      * Called before drag operations to get the current size of the resizing element. 
36993      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36994      */
36995      getElementSize : function(s){
36996         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36997             return s.resizingEl.getWidth();
36998         }else{
36999             return s.resizingEl.getHeight();
37000         }
37001     },
37002     
37003     /**
37004      * Called after drag operations to set the size of the resizing element.
37005      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37006      * @param {Number} newSize The new size to set
37007      * @param {Function} onComplete A function to be invoked when resizing is complete
37008      */
37009     setElementSize : function(s, newSize, onComplete){
37010         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37011             if(!s.animate){
37012                 s.resizingEl.setWidth(newSize);
37013                 if(onComplete){
37014                     onComplete(s, newSize);
37015                 }
37016             }else{
37017                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37018             }
37019         }else{
37020             
37021             if(!s.animate){
37022                 s.resizingEl.setHeight(newSize);
37023                 if(onComplete){
37024                     onComplete(s, newSize);
37025                 }
37026             }else{
37027                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37028             }
37029         }
37030     }
37031 };
37032
37033 /** 
37034  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37035  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37036  * Adapter that  moves the splitter element to align with the resized sizing element. 
37037  * Used with an absolute positioned SplitBar.
37038  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37039  * document.body, make sure you assign an id to the body element.
37040  */
37041 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37042     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37043     this.container = Roo.get(container);
37044 };
37045
37046 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37047     init : function(s){
37048         this.basic.init(s);
37049     },
37050     
37051     getElementSize : function(s){
37052         return this.basic.getElementSize(s);
37053     },
37054     
37055     setElementSize : function(s, newSize, onComplete){
37056         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37057     },
37058     
37059     moveSplitter : function(s){
37060         var yes = Roo.bootstrap.SplitBar;
37061         switch(s.placement){
37062             case yes.LEFT:
37063                 s.el.setX(s.resizingEl.getRight());
37064                 break;
37065             case yes.RIGHT:
37066                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37067                 break;
37068             case yes.TOP:
37069                 s.el.setY(s.resizingEl.getBottom());
37070                 break;
37071             case yes.BOTTOM:
37072                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37073                 break;
37074         }
37075     }
37076 };
37077
37078 /**
37079  * Orientation constant - Create a vertical SplitBar
37080  * @static
37081  * @type Number
37082  */
37083 Roo.bootstrap.SplitBar.VERTICAL = 1;
37084
37085 /**
37086  * Orientation constant - Create a horizontal SplitBar
37087  * @static
37088  * @type Number
37089  */
37090 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37091
37092 /**
37093  * Placement constant - The resizing element is to the left of the splitter element
37094  * @static
37095  * @type Number
37096  */
37097 Roo.bootstrap.SplitBar.LEFT = 1;
37098
37099 /**
37100  * Placement constant - The resizing element is to the right of the splitter element
37101  * @static
37102  * @type Number
37103  */
37104 Roo.bootstrap.SplitBar.RIGHT = 2;
37105
37106 /**
37107  * Placement constant - The resizing element is positioned above the splitter element
37108  * @static
37109  * @type Number
37110  */
37111 Roo.bootstrap.SplitBar.TOP = 3;
37112
37113 /**
37114  * Placement constant - The resizing element is positioned under splitter element
37115  * @static
37116  * @type Number
37117  */
37118 Roo.bootstrap.SplitBar.BOTTOM = 4;
37119 Roo.namespace("Roo.bootstrap.layout");/*
37120  * Based on:
37121  * Ext JS Library 1.1.1
37122  * Copyright(c) 2006-2007, Ext JS, LLC.
37123  *
37124  * Originally Released Under LGPL - original licence link has changed is not relivant.
37125  *
37126  * Fork - LGPL
37127  * <script type="text/javascript">
37128  */
37129
37130 /**
37131  * @class Roo.bootstrap.layout.Manager
37132  * @extends Roo.bootstrap.Component
37133  * Base class for layout managers.
37134  */
37135 Roo.bootstrap.layout.Manager = function(config)
37136 {
37137     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37138
37139
37140
37141
37142
37143     /** false to disable window resize monitoring @type Boolean */
37144     this.monitorWindowResize = true;
37145     this.regions = {};
37146     this.addEvents({
37147         /**
37148          * @event layout
37149          * Fires when a layout is performed.
37150          * @param {Roo.LayoutManager} this
37151          */
37152         "layout" : true,
37153         /**
37154          * @event regionresized
37155          * Fires when the user resizes a region.
37156          * @param {Roo.LayoutRegion} region The resized region
37157          * @param {Number} newSize The new size (width for east/west, height for north/south)
37158          */
37159         "regionresized" : true,
37160         /**
37161          * @event regioncollapsed
37162          * Fires when a region is collapsed.
37163          * @param {Roo.LayoutRegion} region The collapsed region
37164          */
37165         "regioncollapsed" : true,
37166         /**
37167          * @event regionexpanded
37168          * Fires when a region is expanded.
37169          * @param {Roo.LayoutRegion} region The expanded region
37170          */
37171         "regionexpanded" : true
37172     });
37173     this.updating = false;
37174
37175     if (config.el) {
37176         this.el = Roo.get(config.el);
37177         this.initEvents();
37178     }
37179
37180 };
37181
37182 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37183
37184
37185     regions : null,
37186
37187     monitorWindowResize : true,
37188
37189
37190     updating : false,
37191
37192
37193     onRender : function(ct, position)
37194     {
37195         if(!this.el){
37196             this.el = Roo.get(ct);
37197             this.initEvents();
37198         }
37199         //this.fireEvent('render',this);
37200     },
37201
37202
37203     initEvents: function()
37204     {
37205
37206
37207         // ie scrollbar fix
37208         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37209             document.body.scroll = "no";
37210         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37211             this.el.position('relative');
37212         }
37213         this.id = this.el.id;
37214         this.el.addClass("roo-layout-container");
37215         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37216         if(this.el.dom != document.body ) {
37217             this.el.on('resize', this.layout,this);
37218             this.el.on('show', this.layout,this);
37219         }
37220
37221     },
37222
37223     /**
37224      * Returns true if this layout is currently being updated
37225      * @return {Boolean}
37226      */
37227     isUpdating : function(){
37228         return this.updating;
37229     },
37230
37231     /**
37232      * Suspend the LayoutManager from doing auto-layouts while
37233      * making multiple add or remove calls
37234      */
37235     beginUpdate : function(){
37236         this.updating = true;
37237     },
37238
37239     /**
37240      * Restore auto-layouts and optionally disable the manager from performing a layout
37241      * @param {Boolean} noLayout true to disable a layout update
37242      */
37243     endUpdate : function(noLayout){
37244         this.updating = false;
37245         if(!noLayout){
37246             this.layout();
37247         }
37248     },
37249
37250     layout: function(){
37251         // abstract...
37252     },
37253
37254     onRegionResized : function(region, newSize){
37255         this.fireEvent("regionresized", region, newSize);
37256         this.layout();
37257     },
37258
37259     onRegionCollapsed : function(region){
37260         this.fireEvent("regioncollapsed", region);
37261     },
37262
37263     onRegionExpanded : function(region){
37264         this.fireEvent("regionexpanded", region);
37265     },
37266
37267     /**
37268      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37269      * performs box-model adjustments.
37270      * @return {Object} The size as an object {width: (the width), height: (the height)}
37271      */
37272     getViewSize : function()
37273     {
37274         var size;
37275         if(this.el.dom != document.body){
37276             size = this.el.getSize();
37277         }else{
37278             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37279         }
37280         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37281         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37282         return size;
37283     },
37284
37285     /**
37286      * Returns the Element this layout is bound to.
37287      * @return {Roo.Element}
37288      */
37289     getEl : function(){
37290         return this.el;
37291     },
37292
37293     /**
37294      * Returns the specified region.
37295      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37296      * @return {Roo.LayoutRegion}
37297      */
37298     getRegion : function(target){
37299         return this.regions[target.toLowerCase()];
37300     },
37301
37302     onWindowResize : function(){
37303         if(this.monitorWindowResize){
37304             this.layout();
37305         }
37306     }
37307 });
37308 /*
37309  * Based on:
37310  * Ext JS Library 1.1.1
37311  * Copyright(c) 2006-2007, Ext JS, LLC.
37312  *
37313  * Originally Released Under LGPL - original licence link has changed is not relivant.
37314  *
37315  * Fork - LGPL
37316  * <script type="text/javascript">
37317  */
37318 /**
37319  * @class Roo.bootstrap.layout.Border
37320  * @extends Roo.bootstrap.layout.Manager
37321  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37322  * please see: examples/bootstrap/nested.html<br><br>
37323  
37324 <b>The container the layout is rendered into can be either the body element or any other element.
37325 If it is not the body element, the container needs to either be an absolute positioned element,
37326 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37327 the container size if it is not the body element.</b>
37328
37329 * @constructor
37330 * Create a new Border
37331 * @param {Object} config Configuration options
37332  */
37333 Roo.bootstrap.layout.Border = function(config){
37334     config = config || {};
37335     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37336     
37337     
37338     
37339     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37340         if(config[region]){
37341             config[region].region = region;
37342             this.addRegion(config[region]);
37343         }
37344     },this);
37345     
37346 };
37347
37348 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37349
37350 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37351     
37352     parent : false, // this might point to a 'nest' or a ???
37353     
37354     /**
37355      * Creates and adds a new region if it doesn't already exist.
37356      * @param {String} target The target region key (north, south, east, west or center).
37357      * @param {Object} config The regions config object
37358      * @return {BorderLayoutRegion} The new region
37359      */
37360     addRegion : function(config)
37361     {
37362         if(!this.regions[config.region]){
37363             var r = this.factory(config);
37364             this.bindRegion(r);
37365         }
37366         return this.regions[config.region];
37367     },
37368
37369     // private (kinda)
37370     bindRegion : function(r){
37371         this.regions[r.config.region] = r;
37372         
37373         r.on("visibilitychange",    this.layout, this);
37374         r.on("paneladded",          this.layout, this);
37375         r.on("panelremoved",        this.layout, this);
37376         r.on("invalidated",         this.layout, this);
37377         r.on("resized",             this.onRegionResized, this);
37378         r.on("collapsed",           this.onRegionCollapsed, this);
37379         r.on("expanded",            this.onRegionExpanded, this);
37380     },
37381
37382     /**
37383      * Performs a layout update.
37384      */
37385     layout : function()
37386     {
37387         if(this.updating) {
37388             return;
37389         }
37390         
37391         // render all the rebions if they have not been done alreayd?
37392         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37393             if(this.regions[region] && !this.regions[region].bodyEl){
37394                 this.regions[region].onRender(this.el)
37395             }
37396         },this);
37397         
37398         var size = this.getViewSize();
37399         var w = size.width;
37400         var h = size.height;
37401         var centerW = w;
37402         var centerH = h;
37403         var centerY = 0;
37404         var centerX = 0;
37405         //var x = 0, y = 0;
37406
37407         var rs = this.regions;
37408         var north = rs["north"];
37409         var south = rs["south"]; 
37410         var west = rs["west"];
37411         var east = rs["east"];
37412         var center = rs["center"];
37413         //if(this.hideOnLayout){ // not supported anymore
37414             //c.el.setStyle("display", "none");
37415         //}
37416         if(north && north.isVisible()){
37417             var b = north.getBox();
37418             var m = north.getMargins();
37419             b.width = w - (m.left+m.right);
37420             b.x = m.left;
37421             b.y = m.top;
37422             centerY = b.height + b.y + m.bottom;
37423             centerH -= centerY;
37424             north.updateBox(this.safeBox(b));
37425         }
37426         if(south && south.isVisible()){
37427             var b = south.getBox();
37428             var m = south.getMargins();
37429             b.width = w - (m.left+m.right);
37430             b.x = m.left;
37431             var totalHeight = (b.height + m.top + m.bottom);
37432             b.y = h - totalHeight + m.top;
37433             centerH -= totalHeight;
37434             south.updateBox(this.safeBox(b));
37435         }
37436         if(west && west.isVisible()){
37437             var b = west.getBox();
37438             var m = west.getMargins();
37439             b.height = centerH - (m.top+m.bottom);
37440             b.x = m.left;
37441             b.y = centerY + m.top;
37442             var totalWidth = (b.width + m.left + m.right);
37443             centerX += totalWidth;
37444             centerW -= totalWidth;
37445             west.updateBox(this.safeBox(b));
37446         }
37447         if(east && east.isVisible()){
37448             var b = east.getBox();
37449             var m = east.getMargins();
37450             b.height = centerH - (m.top+m.bottom);
37451             var totalWidth = (b.width + m.left + m.right);
37452             b.x = w - totalWidth + m.left;
37453             b.y = centerY + m.top;
37454             centerW -= totalWidth;
37455             east.updateBox(this.safeBox(b));
37456         }
37457         if(center){
37458             var m = center.getMargins();
37459             var centerBox = {
37460                 x: centerX + m.left,
37461                 y: centerY + m.top,
37462                 width: centerW - (m.left+m.right),
37463                 height: centerH - (m.top+m.bottom)
37464             };
37465             //if(this.hideOnLayout){
37466                 //center.el.setStyle("display", "block");
37467             //}
37468             center.updateBox(this.safeBox(centerBox));
37469         }
37470         this.el.repaint();
37471         this.fireEvent("layout", this);
37472     },
37473
37474     // private
37475     safeBox : function(box){
37476         box.width = Math.max(0, box.width);
37477         box.height = Math.max(0, box.height);
37478         return box;
37479     },
37480
37481     /**
37482      * Adds a ContentPanel (or subclass) to this layout.
37483      * @param {String} target The target region key (north, south, east, west or center).
37484      * @param {Roo.ContentPanel} panel The panel to add
37485      * @return {Roo.ContentPanel} The added panel
37486      */
37487     add : function(target, panel){
37488          
37489         target = target.toLowerCase();
37490         return this.regions[target].add(panel);
37491     },
37492
37493     /**
37494      * Remove a ContentPanel (or subclass) to this layout.
37495      * @param {String} target The target region key (north, south, east, west or center).
37496      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37497      * @return {Roo.ContentPanel} The removed panel
37498      */
37499     remove : function(target, panel){
37500         target = target.toLowerCase();
37501         return this.regions[target].remove(panel);
37502     },
37503
37504     /**
37505      * Searches all regions for a panel with the specified id
37506      * @param {String} panelId
37507      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37508      */
37509     findPanel : function(panelId){
37510         var rs = this.regions;
37511         for(var target in rs){
37512             if(typeof rs[target] != "function"){
37513                 var p = rs[target].getPanel(panelId);
37514                 if(p){
37515                     return p;
37516                 }
37517             }
37518         }
37519         return null;
37520     },
37521
37522     /**
37523      * Searches all regions for a panel with the specified id and activates (shows) it.
37524      * @param {String/ContentPanel} panelId The panels id or the panel itself
37525      * @return {Roo.ContentPanel} The shown panel or null
37526      */
37527     showPanel : function(panelId) {
37528       var rs = this.regions;
37529       for(var target in rs){
37530          var r = rs[target];
37531          if(typeof r != "function"){
37532             if(r.hasPanel(panelId)){
37533                return r.showPanel(panelId);
37534             }
37535          }
37536       }
37537       return null;
37538    },
37539
37540    /**
37541      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37542      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37543      */
37544    /*
37545     restoreState : function(provider){
37546         if(!provider){
37547             provider = Roo.state.Manager;
37548         }
37549         var sm = new Roo.LayoutStateManager();
37550         sm.init(this, provider);
37551     },
37552 */
37553  
37554  
37555     /**
37556      * Adds a xtype elements to the layout.
37557      * <pre><code>
37558
37559 layout.addxtype({
37560        xtype : 'ContentPanel',
37561        region: 'west',
37562        items: [ .... ]
37563    }
37564 );
37565
37566 layout.addxtype({
37567         xtype : 'NestedLayoutPanel',
37568         region: 'west',
37569         layout: {
37570            center: { },
37571            west: { }   
37572         },
37573         items : [ ... list of content panels or nested layout panels.. ]
37574    }
37575 );
37576 </code></pre>
37577      * @param {Object} cfg Xtype definition of item to add.
37578      */
37579     addxtype : function(cfg)
37580     {
37581         // basically accepts a pannel...
37582         // can accept a layout region..!?!?
37583         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37584         
37585         
37586         // theory?  children can only be panels??
37587         
37588         //if (!cfg.xtype.match(/Panel$/)) {
37589         //    return false;
37590         //}
37591         var ret = false;
37592         
37593         if (typeof(cfg.region) == 'undefined') {
37594             Roo.log("Failed to add Panel, region was not set");
37595             Roo.log(cfg);
37596             return false;
37597         }
37598         var region = cfg.region;
37599         delete cfg.region;
37600         
37601           
37602         var xitems = [];
37603         if (cfg.items) {
37604             xitems = cfg.items;
37605             delete cfg.items;
37606         }
37607         var nb = false;
37608         
37609         if ( region == 'center') {
37610             Roo.log("Center: " + cfg.title);
37611         }
37612         
37613         
37614         switch(cfg.xtype) 
37615         {
37616             case 'Content':  // ContentPanel (el, cfg)
37617             case 'Scroll':  // ContentPanel (el, cfg)
37618             case 'View': 
37619                 cfg.autoCreate = cfg.autoCreate || true;
37620                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37621                 //} else {
37622                 //    var el = this.el.createChild();
37623                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37624                 //}
37625                 
37626                 this.add(region, ret);
37627                 break;
37628             
37629             /*
37630             case 'TreePanel': // our new panel!
37631                 cfg.el = this.el.createChild();
37632                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37633                 this.add(region, ret);
37634                 break;
37635             */
37636             
37637             case 'Nest': 
37638                 // create a new Layout (which is  a Border Layout...
37639                 
37640                 var clayout = cfg.layout;
37641                 clayout.el  = this.el.createChild();
37642                 clayout.items   = clayout.items  || [];
37643                 
37644                 delete cfg.layout;
37645                 
37646                 // replace this exitems with the clayout ones..
37647                 xitems = clayout.items;
37648                  
37649                 // force background off if it's in center...
37650                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37651                     cfg.background = false;
37652                 }
37653                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37654                 
37655                 
37656                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37657                 //console.log('adding nested layout panel '  + cfg.toSource());
37658                 this.add(region, ret);
37659                 nb = {}; /// find first...
37660                 break;
37661             
37662             case 'Grid':
37663                 
37664                 // needs grid and region
37665                 
37666                 //var el = this.getRegion(region).el.createChild();
37667                 /*
37668                  *var el = this.el.createChild();
37669                 // create the grid first...
37670                 cfg.grid.container = el;
37671                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37672                 */
37673                 
37674                 if (region == 'center' && this.active ) {
37675                     cfg.background = false;
37676                 }
37677                 
37678                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37679                 
37680                 this.add(region, ret);
37681                 /*
37682                 if (cfg.background) {
37683                     // render grid on panel activation (if panel background)
37684                     ret.on('activate', function(gp) {
37685                         if (!gp.grid.rendered) {
37686                     //        gp.grid.render(el);
37687                         }
37688                     });
37689                 } else {
37690                   //  cfg.grid.render(el);
37691                 }
37692                 */
37693                 break;
37694            
37695            
37696             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37697                 // it was the old xcomponent building that caused this before.
37698                 // espeically if border is the top element in the tree.
37699                 ret = this;
37700                 break; 
37701                 
37702                     
37703                 
37704                 
37705                 
37706             default:
37707                 /*
37708                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37709                     
37710                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37711                     this.add(region, ret);
37712                 } else {
37713                 */
37714                     Roo.log(cfg);
37715                     throw "Can not add '" + cfg.xtype + "' to Border";
37716                     return null;
37717              
37718                                 
37719              
37720         }
37721         this.beginUpdate();
37722         // add children..
37723         var region = '';
37724         var abn = {};
37725         Roo.each(xitems, function(i)  {
37726             region = nb && i.region ? i.region : false;
37727             
37728             var add = ret.addxtype(i);
37729            
37730             if (region) {
37731                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37732                 if (!i.background) {
37733                     abn[region] = nb[region] ;
37734                 }
37735             }
37736             
37737         });
37738         this.endUpdate();
37739
37740         // make the last non-background panel active..
37741         //if (nb) { Roo.log(abn); }
37742         if (nb) {
37743             
37744             for(var r in abn) {
37745                 region = this.getRegion(r);
37746                 if (region) {
37747                     // tried using nb[r], but it does not work..
37748                      
37749                     region.showPanel(abn[r]);
37750                    
37751                 }
37752             }
37753         }
37754         return ret;
37755         
37756     },
37757     
37758     
37759 // private
37760     factory : function(cfg)
37761     {
37762         
37763         var validRegions = Roo.bootstrap.layout.Border.regions;
37764
37765         var target = cfg.region;
37766         cfg.mgr = this;
37767         
37768         var r = Roo.bootstrap.layout;
37769         Roo.log(target);
37770         switch(target){
37771             case "north":
37772                 return new r.North(cfg);
37773             case "south":
37774                 return new r.South(cfg);
37775             case "east":
37776                 return new r.East(cfg);
37777             case "west":
37778                 return new r.West(cfg);
37779             case "center":
37780                 return new r.Center(cfg);
37781         }
37782         throw 'Layout region "'+target+'" not supported.';
37783     }
37784     
37785     
37786 });
37787  /*
37788  * Based on:
37789  * Ext JS Library 1.1.1
37790  * Copyright(c) 2006-2007, Ext JS, LLC.
37791  *
37792  * Originally Released Under LGPL - original licence link has changed is not relivant.
37793  *
37794  * Fork - LGPL
37795  * <script type="text/javascript">
37796  */
37797  
37798 /**
37799  * @class Roo.bootstrap.layout.Basic
37800  * @extends Roo.util.Observable
37801  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37802  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37803  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37804  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37805  * @cfg {string}   region  the region that it inhabits..
37806  * @cfg {bool}   skipConfig skip config?
37807  * 
37808
37809  */
37810 Roo.bootstrap.layout.Basic = function(config){
37811     
37812     this.mgr = config.mgr;
37813     
37814     this.position = config.region;
37815     
37816     var skipConfig = config.skipConfig;
37817     
37818     this.events = {
37819         /**
37820          * @scope Roo.BasicLayoutRegion
37821          */
37822         
37823         /**
37824          * @event beforeremove
37825          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37826          * @param {Roo.LayoutRegion} this
37827          * @param {Roo.ContentPanel} panel The panel
37828          * @param {Object} e The cancel event object
37829          */
37830         "beforeremove" : true,
37831         /**
37832          * @event invalidated
37833          * Fires when the layout for this region is changed.
37834          * @param {Roo.LayoutRegion} this
37835          */
37836         "invalidated" : true,
37837         /**
37838          * @event visibilitychange
37839          * Fires when this region is shown or hidden 
37840          * @param {Roo.LayoutRegion} this
37841          * @param {Boolean} visibility true or false
37842          */
37843         "visibilitychange" : true,
37844         /**
37845          * @event paneladded
37846          * Fires when a panel is added. 
37847          * @param {Roo.LayoutRegion} this
37848          * @param {Roo.ContentPanel} panel The panel
37849          */
37850         "paneladded" : true,
37851         /**
37852          * @event panelremoved
37853          * Fires when a panel is removed. 
37854          * @param {Roo.LayoutRegion} this
37855          * @param {Roo.ContentPanel} panel The panel
37856          */
37857         "panelremoved" : true,
37858         /**
37859          * @event beforecollapse
37860          * Fires when this region before collapse.
37861          * @param {Roo.LayoutRegion} this
37862          */
37863         "beforecollapse" : true,
37864         /**
37865          * @event collapsed
37866          * Fires when this region is collapsed.
37867          * @param {Roo.LayoutRegion} this
37868          */
37869         "collapsed" : true,
37870         /**
37871          * @event expanded
37872          * Fires when this region is expanded.
37873          * @param {Roo.LayoutRegion} this
37874          */
37875         "expanded" : true,
37876         /**
37877          * @event slideshow
37878          * Fires when this region is slid into view.
37879          * @param {Roo.LayoutRegion} this
37880          */
37881         "slideshow" : true,
37882         /**
37883          * @event slidehide
37884          * Fires when this region slides out of view. 
37885          * @param {Roo.LayoutRegion} this
37886          */
37887         "slidehide" : true,
37888         /**
37889          * @event panelactivated
37890          * Fires when a panel is activated. 
37891          * @param {Roo.LayoutRegion} this
37892          * @param {Roo.ContentPanel} panel The activated panel
37893          */
37894         "panelactivated" : true,
37895         /**
37896          * @event resized
37897          * Fires when the user resizes this region. 
37898          * @param {Roo.LayoutRegion} this
37899          * @param {Number} newSize The new size (width for east/west, height for north/south)
37900          */
37901         "resized" : true
37902     };
37903     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37904     this.panels = new Roo.util.MixedCollection();
37905     this.panels.getKey = this.getPanelId.createDelegate(this);
37906     this.box = null;
37907     this.activePanel = null;
37908     // ensure listeners are added...
37909     
37910     if (config.listeners || config.events) {
37911         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37912             listeners : config.listeners || {},
37913             events : config.events || {}
37914         });
37915     }
37916     
37917     if(skipConfig !== true){
37918         this.applyConfig(config);
37919     }
37920 };
37921
37922 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37923 {
37924     getPanelId : function(p){
37925         return p.getId();
37926     },
37927     
37928     applyConfig : function(config){
37929         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37930         this.config = config;
37931         
37932     },
37933     
37934     /**
37935      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37936      * the width, for horizontal (north, south) the height.
37937      * @param {Number} newSize The new width or height
37938      */
37939     resizeTo : function(newSize){
37940         var el = this.el ? this.el :
37941                  (this.activePanel ? this.activePanel.getEl() : null);
37942         if(el){
37943             switch(this.position){
37944                 case "east":
37945                 case "west":
37946                     el.setWidth(newSize);
37947                     this.fireEvent("resized", this, newSize);
37948                 break;
37949                 case "north":
37950                 case "south":
37951                     el.setHeight(newSize);
37952                     this.fireEvent("resized", this, newSize);
37953                 break;                
37954             }
37955         }
37956     },
37957     
37958     getBox : function(){
37959         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37960     },
37961     
37962     getMargins : function(){
37963         return this.margins;
37964     },
37965     
37966     updateBox : function(box){
37967         this.box = box;
37968         var el = this.activePanel.getEl();
37969         el.dom.style.left = box.x + "px";
37970         el.dom.style.top = box.y + "px";
37971         this.activePanel.setSize(box.width, box.height);
37972     },
37973     
37974     /**
37975      * Returns the container element for this region.
37976      * @return {Roo.Element}
37977      */
37978     getEl : function(){
37979         return this.activePanel;
37980     },
37981     
37982     /**
37983      * Returns true if this region is currently visible.
37984      * @return {Boolean}
37985      */
37986     isVisible : function(){
37987         return this.activePanel ? true : false;
37988     },
37989     
37990     setActivePanel : function(panel){
37991         panel = this.getPanel(panel);
37992         if(this.activePanel && this.activePanel != panel){
37993             this.activePanel.setActiveState(false);
37994             this.activePanel.getEl().setLeftTop(-10000,-10000);
37995         }
37996         this.activePanel = panel;
37997         panel.setActiveState(true);
37998         if(this.box){
37999             panel.setSize(this.box.width, this.box.height);
38000         }
38001         this.fireEvent("panelactivated", this, panel);
38002         this.fireEvent("invalidated");
38003     },
38004     
38005     /**
38006      * Show the specified panel.
38007      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38008      * @return {Roo.ContentPanel} The shown panel or null
38009      */
38010     showPanel : function(panel){
38011         panel = this.getPanel(panel);
38012         if(panel){
38013             this.setActivePanel(panel);
38014         }
38015         return panel;
38016     },
38017     
38018     /**
38019      * Get the active panel for this region.
38020      * @return {Roo.ContentPanel} The active panel or null
38021      */
38022     getActivePanel : function(){
38023         return this.activePanel;
38024     },
38025     
38026     /**
38027      * Add the passed ContentPanel(s)
38028      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38029      * @return {Roo.ContentPanel} The panel added (if only one was added)
38030      */
38031     add : function(panel){
38032         if(arguments.length > 1){
38033             for(var i = 0, len = arguments.length; i < len; i++) {
38034                 this.add(arguments[i]);
38035             }
38036             return null;
38037         }
38038         if(this.hasPanel(panel)){
38039             this.showPanel(panel);
38040             return panel;
38041         }
38042         var el = panel.getEl();
38043         if(el.dom.parentNode != this.mgr.el.dom){
38044             this.mgr.el.dom.appendChild(el.dom);
38045         }
38046         if(panel.setRegion){
38047             panel.setRegion(this);
38048         }
38049         this.panels.add(panel);
38050         el.setStyle("position", "absolute");
38051         if(!panel.background){
38052             this.setActivePanel(panel);
38053             if(this.config.initialSize && this.panels.getCount()==1){
38054                 this.resizeTo(this.config.initialSize);
38055             }
38056         }
38057         this.fireEvent("paneladded", this, panel);
38058         return panel;
38059     },
38060     
38061     /**
38062      * Returns true if the panel is in this region.
38063      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38064      * @return {Boolean}
38065      */
38066     hasPanel : function(panel){
38067         if(typeof panel == "object"){ // must be panel obj
38068             panel = panel.getId();
38069         }
38070         return this.getPanel(panel) ? true : false;
38071     },
38072     
38073     /**
38074      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38075      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38076      * @param {Boolean} preservePanel Overrides the config preservePanel option
38077      * @return {Roo.ContentPanel} The panel that was removed
38078      */
38079     remove : function(panel, preservePanel){
38080         panel = this.getPanel(panel);
38081         if(!panel){
38082             return null;
38083         }
38084         var e = {};
38085         this.fireEvent("beforeremove", this, panel, e);
38086         if(e.cancel === true){
38087             return null;
38088         }
38089         var panelId = panel.getId();
38090         this.panels.removeKey(panelId);
38091         return panel;
38092     },
38093     
38094     /**
38095      * Returns the panel specified or null if it's not in this region.
38096      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38097      * @return {Roo.ContentPanel}
38098      */
38099     getPanel : function(id){
38100         if(typeof id == "object"){ // must be panel obj
38101             return id;
38102         }
38103         return this.panels.get(id);
38104     },
38105     
38106     /**
38107      * Returns this regions position (north/south/east/west/center).
38108      * @return {String} 
38109      */
38110     getPosition: function(){
38111         return this.position;    
38112     }
38113 });/*
38114  * Based on:
38115  * Ext JS Library 1.1.1
38116  * Copyright(c) 2006-2007, Ext JS, LLC.
38117  *
38118  * Originally Released Under LGPL - original licence link has changed is not relivant.
38119  *
38120  * Fork - LGPL
38121  * <script type="text/javascript">
38122  */
38123  
38124 /**
38125  * @class Roo.bootstrap.layout.Region
38126  * @extends Roo.bootstrap.layout.Basic
38127  * This class represents a region in a layout manager.
38128  
38129  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38130  * @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})
38131  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38132  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38133  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38134  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38135  * @cfg {String}    title           The title for the region (overrides panel titles)
38136  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38137  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38138  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38139  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38140  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38141  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38142  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38143  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38144  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38145  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38146
38147  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38148  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38149  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38150  * @cfg {Number}    width           For East/West panels
38151  * @cfg {Number}    height          For North/South panels
38152  * @cfg {Boolean}   split           To show the splitter
38153  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38154  * 
38155  * @cfg {string}   cls             Extra CSS classes to add to region
38156  * 
38157  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38158  * @cfg {string}   region  the region that it inhabits..
38159  *
38160
38161  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38162  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38163
38164  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38165  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38166  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38167  */
38168 Roo.bootstrap.layout.Region = function(config)
38169 {
38170     this.applyConfig(config);
38171
38172     var mgr = config.mgr;
38173     var pos = config.region;
38174     config.skipConfig = true;
38175     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38176     
38177     if (mgr.el) {
38178         this.onRender(mgr.el);   
38179     }
38180      
38181     this.visible = true;
38182     this.collapsed = false;
38183     this.unrendered_panels = [];
38184 };
38185
38186 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38187
38188     position: '', // set by wrapper (eg. north/south etc..)
38189     unrendered_panels : null,  // unrendered panels.
38190     
38191     tabPosition : false,
38192     
38193     mgr: false, // points to 'Border'
38194     
38195     
38196     createBody : function(){
38197         /** This region's body element 
38198         * @type Roo.Element */
38199         this.bodyEl = this.el.createChild({
38200                 tag: "div",
38201                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38202         });
38203     },
38204
38205     onRender: function(ctr, pos)
38206     {
38207         var dh = Roo.DomHelper;
38208         /** This region's container element 
38209         * @type Roo.Element */
38210         this.el = dh.append(ctr.dom, {
38211                 tag: "div",
38212                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38213             }, true);
38214         /** This region's title element 
38215         * @type Roo.Element */
38216     
38217         this.titleEl = dh.append(this.el.dom,  {
38218                 tag: "div",
38219                 unselectable: "on",
38220                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38221                 children:[
38222                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38223                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38224                 ]
38225             }, true);
38226         
38227         this.titleEl.enableDisplayMode();
38228         /** This region's title text element 
38229         * @type HTMLElement */
38230         this.titleTextEl = this.titleEl.dom.firstChild;
38231         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38232         /*
38233         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38234         this.closeBtn.enableDisplayMode();
38235         this.closeBtn.on("click", this.closeClicked, this);
38236         this.closeBtn.hide();
38237     */
38238         this.createBody(this.config);
38239         if(this.config.hideWhenEmpty){
38240             this.hide();
38241             this.on("paneladded", this.validateVisibility, this);
38242             this.on("panelremoved", this.validateVisibility, this);
38243         }
38244         if(this.autoScroll){
38245             this.bodyEl.setStyle("overflow", "auto");
38246         }else{
38247             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38248         }
38249         //if(c.titlebar !== false){
38250             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38251                 this.titleEl.hide();
38252             }else{
38253                 this.titleEl.show();
38254                 if(this.config.title){
38255                     this.titleTextEl.innerHTML = this.config.title;
38256                 }
38257             }
38258         //}
38259         if(this.config.collapsed){
38260             this.collapse(true);
38261         }
38262         if(this.config.hidden){
38263             this.hide();
38264         }
38265         
38266         if (this.unrendered_panels && this.unrendered_panels.length) {
38267             for (var i =0;i< this.unrendered_panels.length; i++) {
38268                 this.add(this.unrendered_panels[i]);
38269             }
38270             this.unrendered_panels = null;
38271             
38272         }
38273         
38274     },
38275     
38276     applyConfig : function(c)
38277     {
38278         /*
38279          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38280             var dh = Roo.DomHelper;
38281             if(c.titlebar !== false){
38282                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38283                 this.collapseBtn.on("click", this.collapse, this);
38284                 this.collapseBtn.enableDisplayMode();
38285                 /*
38286                 if(c.showPin === true || this.showPin){
38287                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38288                     this.stickBtn.enableDisplayMode();
38289                     this.stickBtn.on("click", this.expand, this);
38290                     this.stickBtn.hide();
38291                 }
38292                 
38293             }
38294             */
38295             /** This region's collapsed element
38296             * @type Roo.Element */
38297             /*
38298              *
38299             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38300                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38301             ]}, true);
38302             
38303             if(c.floatable !== false){
38304                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38305                this.collapsedEl.on("click", this.collapseClick, this);
38306             }
38307
38308             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38309                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38310                    id: "message", unselectable: "on", style:{"float":"left"}});
38311                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38312              }
38313             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38314             this.expandBtn.on("click", this.expand, this);
38315             
38316         }
38317         
38318         if(this.collapseBtn){
38319             this.collapseBtn.setVisible(c.collapsible == true);
38320         }
38321         
38322         this.cmargins = c.cmargins || this.cmargins ||
38323                          (this.position == "west" || this.position == "east" ?
38324                              {top: 0, left: 2, right:2, bottom: 0} :
38325                              {top: 2, left: 0, right:0, bottom: 2});
38326         */
38327         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38328         
38329         
38330         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38331         
38332         this.autoScroll = c.autoScroll || false;
38333         
38334         
38335        
38336         
38337         this.duration = c.duration || .30;
38338         this.slideDuration = c.slideDuration || .45;
38339         this.config = c;
38340        
38341     },
38342     /**
38343      * Returns true if this region is currently visible.
38344      * @return {Boolean}
38345      */
38346     isVisible : function(){
38347         return this.visible;
38348     },
38349
38350     /**
38351      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38352      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38353      */
38354     //setCollapsedTitle : function(title){
38355     //    title = title || "&#160;";
38356      //   if(this.collapsedTitleTextEl){
38357       //      this.collapsedTitleTextEl.innerHTML = title;
38358        // }
38359     //},
38360
38361     getBox : function(){
38362         var b;
38363       //  if(!this.collapsed){
38364             b = this.el.getBox(false, true);
38365        // }else{
38366           //  b = this.collapsedEl.getBox(false, true);
38367         //}
38368         return b;
38369     },
38370
38371     getMargins : function(){
38372         return this.margins;
38373         //return this.collapsed ? this.cmargins : this.margins;
38374     },
38375 /*
38376     highlight : function(){
38377         this.el.addClass("x-layout-panel-dragover");
38378     },
38379
38380     unhighlight : function(){
38381         this.el.removeClass("x-layout-panel-dragover");
38382     },
38383 */
38384     updateBox : function(box)
38385     {
38386         if (!this.bodyEl) {
38387             return; // not rendered yet..
38388         }
38389         
38390         this.box = box;
38391         if(!this.collapsed){
38392             this.el.dom.style.left = box.x + "px";
38393             this.el.dom.style.top = box.y + "px";
38394             this.updateBody(box.width, box.height);
38395         }else{
38396             this.collapsedEl.dom.style.left = box.x + "px";
38397             this.collapsedEl.dom.style.top = box.y + "px";
38398             this.collapsedEl.setSize(box.width, box.height);
38399         }
38400         if(this.tabs){
38401             this.tabs.autoSizeTabs();
38402         }
38403     },
38404
38405     updateBody : function(w, h)
38406     {
38407         if(w !== null){
38408             this.el.setWidth(w);
38409             w -= this.el.getBorderWidth("rl");
38410             if(this.config.adjustments){
38411                 w += this.config.adjustments[0];
38412             }
38413         }
38414         if(h !== null && h > 0){
38415             this.el.setHeight(h);
38416             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38417             h -= this.el.getBorderWidth("tb");
38418             if(this.config.adjustments){
38419                 h += this.config.adjustments[1];
38420             }
38421             this.bodyEl.setHeight(h);
38422             if(this.tabs){
38423                 h = this.tabs.syncHeight(h);
38424             }
38425         }
38426         if(this.panelSize){
38427             w = w !== null ? w : this.panelSize.width;
38428             h = h !== null ? h : this.panelSize.height;
38429         }
38430         if(this.activePanel){
38431             var el = this.activePanel.getEl();
38432             w = w !== null ? w : el.getWidth();
38433             h = h !== null ? h : el.getHeight();
38434             this.panelSize = {width: w, height: h};
38435             this.activePanel.setSize(w, h);
38436         }
38437         if(Roo.isIE && this.tabs){
38438             this.tabs.el.repaint();
38439         }
38440     },
38441
38442     /**
38443      * Returns the container element for this region.
38444      * @return {Roo.Element}
38445      */
38446     getEl : function(){
38447         return this.el;
38448     },
38449
38450     /**
38451      * Hides this region.
38452      */
38453     hide : function(){
38454         //if(!this.collapsed){
38455             this.el.dom.style.left = "-2000px";
38456             this.el.hide();
38457         //}else{
38458          //   this.collapsedEl.dom.style.left = "-2000px";
38459          //   this.collapsedEl.hide();
38460        // }
38461         this.visible = false;
38462         this.fireEvent("visibilitychange", this, false);
38463     },
38464
38465     /**
38466      * Shows this region if it was previously hidden.
38467      */
38468     show : function(){
38469         //if(!this.collapsed){
38470             this.el.show();
38471         //}else{
38472         //    this.collapsedEl.show();
38473        // }
38474         this.visible = true;
38475         this.fireEvent("visibilitychange", this, true);
38476     },
38477 /*
38478     closeClicked : function(){
38479         if(this.activePanel){
38480             this.remove(this.activePanel);
38481         }
38482     },
38483
38484     collapseClick : function(e){
38485         if(this.isSlid){
38486            e.stopPropagation();
38487            this.slideIn();
38488         }else{
38489            e.stopPropagation();
38490            this.slideOut();
38491         }
38492     },
38493 */
38494     /**
38495      * Collapses this region.
38496      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38497      */
38498     /*
38499     collapse : function(skipAnim, skipCheck = false){
38500         if(this.collapsed) {
38501             return;
38502         }
38503         
38504         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38505             
38506             this.collapsed = true;
38507             if(this.split){
38508                 this.split.el.hide();
38509             }
38510             if(this.config.animate && skipAnim !== true){
38511                 this.fireEvent("invalidated", this);
38512                 this.animateCollapse();
38513             }else{
38514                 this.el.setLocation(-20000,-20000);
38515                 this.el.hide();
38516                 this.collapsedEl.show();
38517                 this.fireEvent("collapsed", this);
38518                 this.fireEvent("invalidated", this);
38519             }
38520         }
38521         
38522     },
38523 */
38524     animateCollapse : function(){
38525         // overridden
38526     },
38527
38528     /**
38529      * Expands this region if it was previously collapsed.
38530      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38531      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38532      */
38533     /*
38534     expand : function(e, skipAnim){
38535         if(e) {
38536             e.stopPropagation();
38537         }
38538         if(!this.collapsed || this.el.hasActiveFx()) {
38539             return;
38540         }
38541         if(this.isSlid){
38542             this.afterSlideIn();
38543             skipAnim = true;
38544         }
38545         this.collapsed = false;
38546         if(this.config.animate && skipAnim !== true){
38547             this.animateExpand();
38548         }else{
38549             this.el.show();
38550             if(this.split){
38551                 this.split.el.show();
38552             }
38553             this.collapsedEl.setLocation(-2000,-2000);
38554             this.collapsedEl.hide();
38555             this.fireEvent("invalidated", this);
38556             this.fireEvent("expanded", this);
38557         }
38558     },
38559 */
38560     animateExpand : function(){
38561         // overridden
38562     },
38563
38564     initTabs : function()
38565     {
38566         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38567         
38568         var ts = new Roo.bootstrap.panel.Tabs({
38569             el: this.bodyEl.dom,
38570             region : this,
38571             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38572             disableTooltips: this.config.disableTabTips,
38573             toolbar : this.config.toolbar
38574         });
38575         
38576         if(this.config.hideTabs){
38577             ts.stripWrap.setDisplayed(false);
38578         }
38579         this.tabs = ts;
38580         ts.resizeTabs = this.config.resizeTabs === true;
38581         ts.minTabWidth = this.config.minTabWidth || 40;
38582         ts.maxTabWidth = this.config.maxTabWidth || 250;
38583         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38584         ts.monitorResize = false;
38585         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38586         ts.bodyEl.addClass('roo-layout-tabs-body');
38587         this.panels.each(this.initPanelAsTab, this);
38588     },
38589
38590     initPanelAsTab : function(panel){
38591         var ti = this.tabs.addTab(
38592             panel.getEl().id,
38593             panel.getTitle(),
38594             null,
38595             this.config.closeOnTab && panel.isClosable(),
38596             panel.tpl
38597         );
38598         if(panel.tabTip !== undefined){
38599             ti.setTooltip(panel.tabTip);
38600         }
38601         ti.on("activate", function(){
38602               this.setActivePanel(panel);
38603         }, this);
38604         
38605         if(this.config.closeOnTab){
38606             ti.on("beforeclose", function(t, e){
38607                 e.cancel = true;
38608                 this.remove(panel);
38609             }, this);
38610         }
38611         
38612         panel.tabItem = ti;
38613         
38614         return ti;
38615     },
38616
38617     updatePanelTitle : function(panel, title)
38618     {
38619         if(this.activePanel == panel){
38620             this.updateTitle(title);
38621         }
38622         if(this.tabs){
38623             var ti = this.tabs.getTab(panel.getEl().id);
38624             ti.setText(title);
38625             if(panel.tabTip !== undefined){
38626                 ti.setTooltip(panel.tabTip);
38627             }
38628         }
38629     },
38630
38631     updateTitle : function(title){
38632         if(this.titleTextEl && !this.config.title){
38633             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38634         }
38635     },
38636
38637     setActivePanel : function(panel)
38638     {
38639         panel = this.getPanel(panel);
38640         if(this.activePanel && this.activePanel != panel){
38641             if(this.activePanel.setActiveState(false) === false){
38642                 return;
38643             }
38644         }
38645         this.activePanel = panel;
38646         panel.setActiveState(true);
38647         if(this.panelSize){
38648             panel.setSize(this.panelSize.width, this.panelSize.height);
38649         }
38650         if(this.closeBtn){
38651             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38652         }
38653         this.updateTitle(panel.getTitle());
38654         if(this.tabs){
38655             this.fireEvent("invalidated", this);
38656         }
38657         this.fireEvent("panelactivated", this, panel);
38658     },
38659
38660     /**
38661      * Shows the specified panel.
38662      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38663      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38664      */
38665     showPanel : function(panel)
38666     {
38667         panel = this.getPanel(panel);
38668         if(panel){
38669             if(this.tabs){
38670                 var tab = this.tabs.getTab(panel.getEl().id);
38671                 if(tab.isHidden()){
38672                     this.tabs.unhideTab(tab.id);
38673                 }
38674                 tab.activate();
38675             }else{
38676                 this.setActivePanel(panel);
38677             }
38678         }
38679         return panel;
38680     },
38681
38682     /**
38683      * Get the active panel for this region.
38684      * @return {Roo.ContentPanel} The active panel or null
38685      */
38686     getActivePanel : function(){
38687         return this.activePanel;
38688     },
38689
38690     validateVisibility : function(){
38691         if(this.panels.getCount() < 1){
38692             this.updateTitle("&#160;");
38693             this.closeBtn.hide();
38694             this.hide();
38695         }else{
38696             if(!this.isVisible()){
38697                 this.show();
38698             }
38699         }
38700     },
38701
38702     /**
38703      * Adds the passed ContentPanel(s) to this region.
38704      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38705      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38706      */
38707     add : function(panel)
38708     {
38709         if(arguments.length > 1){
38710             for(var i = 0, len = arguments.length; i < len; i++) {
38711                 this.add(arguments[i]);
38712             }
38713             return null;
38714         }
38715         
38716         // if we have not been rendered yet, then we can not really do much of this..
38717         if (!this.bodyEl) {
38718             this.unrendered_panels.push(panel);
38719             return panel;
38720         }
38721         
38722         
38723         
38724         
38725         if(this.hasPanel(panel)){
38726             this.showPanel(panel);
38727             return panel;
38728         }
38729         panel.setRegion(this);
38730         this.panels.add(panel);
38731        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38732             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38733             // and hide them... ???
38734             this.bodyEl.dom.appendChild(panel.getEl().dom);
38735             if(panel.background !== true){
38736                 this.setActivePanel(panel);
38737             }
38738             this.fireEvent("paneladded", this, panel);
38739             return panel;
38740         }
38741         */
38742         if(!this.tabs){
38743             this.initTabs();
38744         }else{
38745             this.initPanelAsTab(panel);
38746         }
38747         
38748         
38749         if(panel.background !== true){
38750             this.tabs.activate(panel.getEl().id);
38751         }
38752         this.fireEvent("paneladded", this, panel);
38753         return panel;
38754     },
38755
38756     /**
38757      * Hides the tab for the specified panel.
38758      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38759      */
38760     hidePanel : function(panel){
38761         if(this.tabs && (panel = this.getPanel(panel))){
38762             this.tabs.hideTab(panel.getEl().id);
38763         }
38764     },
38765
38766     /**
38767      * Unhides the tab for a previously hidden panel.
38768      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38769      */
38770     unhidePanel : function(panel){
38771         if(this.tabs && (panel = this.getPanel(panel))){
38772             this.tabs.unhideTab(panel.getEl().id);
38773         }
38774     },
38775
38776     clearPanels : function(){
38777         while(this.panels.getCount() > 0){
38778              this.remove(this.panels.first());
38779         }
38780     },
38781
38782     /**
38783      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38784      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38785      * @param {Boolean} preservePanel Overrides the config preservePanel option
38786      * @return {Roo.ContentPanel} The panel that was removed
38787      */
38788     remove : function(panel, preservePanel)
38789     {
38790         panel = this.getPanel(panel);
38791         if(!panel){
38792             return null;
38793         }
38794         var e = {};
38795         this.fireEvent("beforeremove", this, panel, e);
38796         if(e.cancel === true){
38797             return null;
38798         }
38799         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38800         var panelId = panel.getId();
38801         this.panels.removeKey(panelId);
38802         if(preservePanel){
38803             document.body.appendChild(panel.getEl().dom);
38804         }
38805         if(this.tabs){
38806             this.tabs.removeTab(panel.getEl().id);
38807         }else if (!preservePanel){
38808             this.bodyEl.dom.removeChild(panel.getEl().dom);
38809         }
38810         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38811             var p = this.panels.first();
38812             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38813             tempEl.appendChild(p.getEl().dom);
38814             this.bodyEl.update("");
38815             this.bodyEl.dom.appendChild(p.getEl().dom);
38816             tempEl = null;
38817             this.updateTitle(p.getTitle());
38818             this.tabs = null;
38819             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38820             this.setActivePanel(p);
38821         }
38822         panel.setRegion(null);
38823         if(this.activePanel == panel){
38824             this.activePanel = null;
38825         }
38826         if(this.config.autoDestroy !== false && preservePanel !== true){
38827             try{panel.destroy();}catch(e){}
38828         }
38829         this.fireEvent("panelremoved", this, panel);
38830         return panel;
38831     },
38832
38833     /**
38834      * Returns the TabPanel component used by this region
38835      * @return {Roo.TabPanel}
38836      */
38837     getTabs : function(){
38838         return this.tabs;
38839     },
38840
38841     createTool : function(parentEl, className){
38842         var btn = Roo.DomHelper.append(parentEl, {
38843             tag: "div",
38844             cls: "x-layout-tools-button",
38845             children: [ {
38846                 tag: "div",
38847                 cls: "roo-layout-tools-button-inner " + className,
38848                 html: "&#160;"
38849             }]
38850         }, true);
38851         btn.addClassOnOver("roo-layout-tools-button-over");
38852         return btn;
38853     }
38854 });/*
38855  * Based on:
38856  * Ext JS Library 1.1.1
38857  * Copyright(c) 2006-2007, Ext JS, LLC.
38858  *
38859  * Originally Released Under LGPL - original licence link has changed is not relivant.
38860  *
38861  * Fork - LGPL
38862  * <script type="text/javascript">
38863  */
38864  
38865
38866
38867 /**
38868  * @class Roo.SplitLayoutRegion
38869  * @extends Roo.LayoutRegion
38870  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38871  */
38872 Roo.bootstrap.layout.Split = function(config){
38873     this.cursor = config.cursor;
38874     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38875 };
38876
38877 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38878 {
38879     splitTip : "Drag to resize.",
38880     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38881     useSplitTips : false,
38882
38883     applyConfig : function(config){
38884         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38885     },
38886     
38887     onRender : function(ctr,pos) {
38888         
38889         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38890         if(!this.config.split){
38891             return;
38892         }
38893         if(!this.split){
38894             
38895             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38896                             tag: "div",
38897                             id: this.el.id + "-split",
38898                             cls: "roo-layout-split roo-layout-split-"+this.position,
38899                             html: "&#160;"
38900             });
38901             /** The SplitBar for this region 
38902             * @type Roo.SplitBar */
38903             // does not exist yet...
38904             Roo.log([this.position, this.orientation]);
38905             
38906             this.split = new Roo.bootstrap.SplitBar({
38907                 dragElement : splitEl,
38908                 resizingElement: this.el,
38909                 orientation : this.orientation
38910             });
38911             
38912             this.split.on("moved", this.onSplitMove, this);
38913             this.split.useShim = this.config.useShim === true;
38914             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38915             if(this.useSplitTips){
38916                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38917             }
38918             //if(config.collapsible){
38919             //    this.split.el.on("dblclick", this.collapse,  this);
38920             //}
38921         }
38922         if(typeof this.config.minSize != "undefined"){
38923             this.split.minSize = this.config.minSize;
38924         }
38925         if(typeof this.config.maxSize != "undefined"){
38926             this.split.maxSize = this.config.maxSize;
38927         }
38928         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38929             this.hideSplitter();
38930         }
38931         
38932     },
38933
38934     getHMaxSize : function(){
38935          var cmax = this.config.maxSize || 10000;
38936          var center = this.mgr.getRegion("center");
38937          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38938     },
38939
38940     getVMaxSize : function(){
38941          var cmax = this.config.maxSize || 10000;
38942          var center = this.mgr.getRegion("center");
38943          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38944     },
38945
38946     onSplitMove : function(split, newSize){
38947         this.fireEvent("resized", this, newSize);
38948     },
38949     
38950     /** 
38951      * Returns the {@link Roo.SplitBar} for this region.
38952      * @return {Roo.SplitBar}
38953      */
38954     getSplitBar : function(){
38955         return this.split;
38956     },
38957     
38958     hide : function(){
38959         this.hideSplitter();
38960         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38961     },
38962
38963     hideSplitter : function(){
38964         if(this.split){
38965             this.split.el.setLocation(-2000,-2000);
38966             this.split.el.hide();
38967         }
38968     },
38969
38970     show : function(){
38971         if(this.split){
38972             this.split.el.show();
38973         }
38974         Roo.bootstrap.layout.Split.superclass.show.call(this);
38975     },
38976     
38977     beforeSlide: function(){
38978         if(Roo.isGecko){// firefox overflow auto bug workaround
38979             this.bodyEl.clip();
38980             if(this.tabs) {
38981                 this.tabs.bodyEl.clip();
38982             }
38983             if(this.activePanel){
38984                 this.activePanel.getEl().clip();
38985                 
38986                 if(this.activePanel.beforeSlide){
38987                     this.activePanel.beforeSlide();
38988                 }
38989             }
38990         }
38991     },
38992     
38993     afterSlide : function(){
38994         if(Roo.isGecko){// firefox overflow auto bug workaround
38995             this.bodyEl.unclip();
38996             if(this.tabs) {
38997                 this.tabs.bodyEl.unclip();
38998             }
38999             if(this.activePanel){
39000                 this.activePanel.getEl().unclip();
39001                 if(this.activePanel.afterSlide){
39002                     this.activePanel.afterSlide();
39003                 }
39004             }
39005         }
39006     },
39007
39008     initAutoHide : function(){
39009         if(this.autoHide !== false){
39010             if(!this.autoHideHd){
39011                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39012                 this.autoHideHd = {
39013                     "mouseout": function(e){
39014                         if(!e.within(this.el, true)){
39015                             st.delay(500);
39016                         }
39017                     },
39018                     "mouseover" : function(e){
39019                         st.cancel();
39020                     },
39021                     scope : this
39022                 };
39023             }
39024             this.el.on(this.autoHideHd);
39025         }
39026     },
39027
39028     clearAutoHide : function(){
39029         if(this.autoHide !== false){
39030             this.el.un("mouseout", this.autoHideHd.mouseout);
39031             this.el.un("mouseover", this.autoHideHd.mouseover);
39032         }
39033     },
39034
39035     clearMonitor : function(){
39036         Roo.get(document).un("click", this.slideInIf, this);
39037     },
39038
39039     // these names are backwards but not changed for compat
39040     slideOut : function(){
39041         if(this.isSlid || this.el.hasActiveFx()){
39042             return;
39043         }
39044         this.isSlid = true;
39045         if(this.collapseBtn){
39046             this.collapseBtn.hide();
39047         }
39048         this.closeBtnState = this.closeBtn.getStyle('display');
39049         this.closeBtn.hide();
39050         if(this.stickBtn){
39051             this.stickBtn.show();
39052         }
39053         this.el.show();
39054         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39055         this.beforeSlide();
39056         this.el.setStyle("z-index", 10001);
39057         this.el.slideIn(this.getSlideAnchor(), {
39058             callback: function(){
39059                 this.afterSlide();
39060                 this.initAutoHide();
39061                 Roo.get(document).on("click", this.slideInIf, this);
39062                 this.fireEvent("slideshow", this);
39063             },
39064             scope: this,
39065             block: true
39066         });
39067     },
39068
39069     afterSlideIn : function(){
39070         this.clearAutoHide();
39071         this.isSlid = false;
39072         this.clearMonitor();
39073         this.el.setStyle("z-index", "");
39074         if(this.collapseBtn){
39075             this.collapseBtn.show();
39076         }
39077         this.closeBtn.setStyle('display', this.closeBtnState);
39078         if(this.stickBtn){
39079             this.stickBtn.hide();
39080         }
39081         this.fireEvent("slidehide", this);
39082     },
39083
39084     slideIn : function(cb){
39085         if(!this.isSlid || this.el.hasActiveFx()){
39086             Roo.callback(cb);
39087             return;
39088         }
39089         this.isSlid = false;
39090         this.beforeSlide();
39091         this.el.slideOut(this.getSlideAnchor(), {
39092             callback: function(){
39093                 this.el.setLeftTop(-10000, -10000);
39094                 this.afterSlide();
39095                 this.afterSlideIn();
39096                 Roo.callback(cb);
39097             },
39098             scope: this,
39099             block: true
39100         });
39101     },
39102     
39103     slideInIf : function(e){
39104         if(!e.within(this.el)){
39105             this.slideIn();
39106         }
39107     },
39108
39109     animateCollapse : function(){
39110         this.beforeSlide();
39111         this.el.setStyle("z-index", 20000);
39112         var anchor = this.getSlideAnchor();
39113         this.el.slideOut(anchor, {
39114             callback : function(){
39115                 this.el.setStyle("z-index", "");
39116                 this.collapsedEl.slideIn(anchor, {duration:.3});
39117                 this.afterSlide();
39118                 this.el.setLocation(-10000,-10000);
39119                 this.el.hide();
39120                 this.fireEvent("collapsed", this);
39121             },
39122             scope: this,
39123             block: true
39124         });
39125     },
39126
39127     animateExpand : function(){
39128         this.beforeSlide();
39129         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39130         this.el.setStyle("z-index", 20000);
39131         this.collapsedEl.hide({
39132             duration:.1
39133         });
39134         this.el.slideIn(this.getSlideAnchor(), {
39135             callback : function(){
39136                 this.el.setStyle("z-index", "");
39137                 this.afterSlide();
39138                 if(this.split){
39139                     this.split.el.show();
39140                 }
39141                 this.fireEvent("invalidated", this);
39142                 this.fireEvent("expanded", this);
39143             },
39144             scope: this,
39145             block: true
39146         });
39147     },
39148
39149     anchors : {
39150         "west" : "left",
39151         "east" : "right",
39152         "north" : "top",
39153         "south" : "bottom"
39154     },
39155
39156     sanchors : {
39157         "west" : "l",
39158         "east" : "r",
39159         "north" : "t",
39160         "south" : "b"
39161     },
39162
39163     canchors : {
39164         "west" : "tl-tr",
39165         "east" : "tr-tl",
39166         "north" : "tl-bl",
39167         "south" : "bl-tl"
39168     },
39169
39170     getAnchor : function(){
39171         return this.anchors[this.position];
39172     },
39173
39174     getCollapseAnchor : function(){
39175         return this.canchors[this.position];
39176     },
39177
39178     getSlideAnchor : function(){
39179         return this.sanchors[this.position];
39180     },
39181
39182     getAlignAdj : function(){
39183         var cm = this.cmargins;
39184         switch(this.position){
39185             case "west":
39186                 return [0, 0];
39187             break;
39188             case "east":
39189                 return [0, 0];
39190             break;
39191             case "north":
39192                 return [0, 0];
39193             break;
39194             case "south":
39195                 return [0, 0];
39196             break;
39197         }
39198     },
39199
39200     getExpandAdj : function(){
39201         var c = this.collapsedEl, cm = this.cmargins;
39202         switch(this.position){
39203             case "west":
39204                 return [-(cm.right+c.getWidth()+cm.left), 0];
39205             break;
39206             case "east":
39207                 return [cm.right+c.getWidth()+cm.left, 0];
39208             break;
39209             case "north":
39210                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39211             break;
39212             case "south":
39213                 return [0, cm.top+cm.bottom+c.getHeight()];
39214             break;
39215         }
39216     }
39217 });/*
39218  * Based on:
39219  * Ext JS Library 1.1.1
39220  * Copyright(c) 2006-2007, Ext JS, LLC.
39221  *
39222  * Originally Released Under LGPL - original licence link has changed is not relivant.
39223  *
39224  * Fork - LGPL
39225  * <script type="text/javascript">
39226  */
39227 /*
39228  * These classes are private internal classes
39229  */
39230 Roo.bootstrap.layout.Center = function(config){
39231     config.region = "center";
39232     Roo.bootstrap.layout.Region.call(this, config);
39233     this.visible = true;
39234     this.minWidth = config.minWidth || 20;
39235     this.minHeight = config.minHeight || 20;
39236 };
39237
39238 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39239     hide : function(){
39240         // center panel can't be hidden
39241     },
39242     
39243     show : function(){
39244         // center panel can't be hidden
39245     },
39246     
39247     getMinWidth: function(){
39248         return this.minWidth;
39249     },
39250     
39251     getMinHeight: function(){
39252         return this.minHeight;
39253     }
39254 });
39255
39256
39257
39258
39259  
39260
39261
39262
39263
39264
39265
39266 Roo.bootstrap.layout.North = function(config)
39267 {
39268     config.region = 'north';
39269     config.cursor = 'n-resize';
39270     
39271     Roo.bootstrap.layout.Split.call(this, config);
39272     
39273     
39274     if(this.split){
39275         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39276         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39277         this.split.el.addClass("roo-layout-split-v");
39278     }
39279     //var size = config.initialSize || config.height;
39280     //if(this.el && typeof size != "undefined"){
39281     //    this.el.setHeight(size);
39282     //}
39283 };
39284 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39285 {
39286     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39287      
39288      
39289     onRender : function(ctr, pos)
39290     {
39291         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39292         var size = this.config.initialSize || this.config.height;
39293         if(this.el && typeof size != "undefined"){
39294             this.el.setHeight(size);
39295         }
39296     
39297     },
39298     
39299     getBox : function(){
39300         if(this.collapsed){
39301             return this.collapsedEl.getBox();
39302         }
39303         var box = this.el.getBox();
39304         if(this.split){
39305             box.height += this.split.el.getHeight();
39306         }
39307         return box;
39308     },
39309     
39310     updateBox : function(box){
39311         if(this.split && !this.collapsed){
39312             box.height -= this.split.el.getHeight();
39313             this.split.el.setLeft(box.x);
39314             this.split.el.setTop(box.y+box.height);
39315             this.split.el.setWidth(box.width);
39316         }
39317         if(this.collapsed){
39318             this.updateBody(box.width, null);
39319         }
39320         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39321     }
39322 });
39323
39324
39325
39326
39327
39328 Roo.bootstrap.layout.South = function(config){
39329     config.region = 'south';
39330     config.cursor = 's-resize';
39331     Roo.bootstrap.layout.Split.call(this, config);
39332     if(this.split){
39333         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39334         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39335         this.split.el.addClass("roo-layout-split-v");
39336     }
39337     
39338 };
39339
39340 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39341     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39342     
39343     onRender : function(ctr, pos)
39344     {
39345         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39346         var size = this.config.initialSize || this.config.height;
39347         if(this.el && typeof size != "undefined"){
39348             this.el.setHeight(size);
39349         }
39350     
39351     },
39352     
39353     getBox : function(){
39354         if(this.collapsed){
39355             return this.collapsedEl.getBox();
39356         }
39357         var box = this.el.getBox();
39358         if(this.split){
39359             var sh = this.split.el.getHeight();
39360             box.height += sh;
39361             box.y -= sh;
39362         }
39363         return box;
39364     },
39365     
39366     updateBox : function(box){
39367         if(this.split && !this.collapsed){
39368             var sh = this.split.el.getHeight();
39369             box.height -= sh;
39370             box.y += sh;
39371             this.split.el.setLeft(box.x);
39372             this.split.el.setTop(box.y-sh);
39373             this.split.el.setWidth(box.width);
39374         }
39375         if(this.collapsed){
39376             this.updateBody(box.width, null);
39377         }
39378         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39379     }
39380 });
39381
39382 Roo.bootstrap.layout.East = function(config){
39383     config.region = "east";
39384     config.cursor = "e-resize";
39385     Roo.bootstrap.layout.Split.call(this, config);
39386     if(this.split){
39387         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39388         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39389         this.split.el.addClass("roo-layout-split-h");
39390     }
39391     
39392 };
39393 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39394     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39395     
39396     onRender : function(ctr, pos)
39397     {
39398         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39399         var size = this.config.initialSize || this.config.width;
39400         if(this.el && typeof size != "undefined"){
39401             this.el.setWidth(size);
39402         }
39403     
39404     },
39405     
39406     getBox : function(){
39407         if(this.collapsed){
39408             return this.collapsedEl.getBox();
39409         }
39410         var box = this.el.getBox();
39411         if(this.split){
39412             var sw = this.split.el.getWidth();
39413             box.width += sw;
39414             box.x -= sw;
39415         }
39416         return box;
39417     },
39418
39419     updateBox : function(box){
39420         if(this.split && !this.collapsed){
39421             var sw = this.split.el.getWidth();
39422             box.width -= sw;
39423             this.split.el.setLeft(box.x);
39424             this.split.el.setTop(box.y);
39425             this.split.el.setHeight(box.height);
39426             box.x += sw;
39427         }
39428         if(this.collapsed){
39429             this.updateBody(null, box.height);
39430         }
39431         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39432     }
39433 });
39434
39435 Roo.bootstrap.layout.West = function(config){
39436     config.region = "west";
39437     config.cursor = "w-resize";
39438     
39439     Roo.bootstrap.layout.Split.call(this, config);
39440     if(this.split){
39441         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39442         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39443         this.split.el.addClass("roo-layout-split-h");
39444     }
39445     
39446 };
39447 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39448     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39449     
39450     onRender: function(ctr, pos)
39451     {
39452         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39453         var size = this.config.initialSize || this.config.width;
39454         if(typeof size != "undefined"){
39455             this.el.setWidth(size);
39456         }
39457     },
39458     
39459     getBox : function(){
39460         if(this.collapsed){
39461             return this.collapsedEl.getBox();
39462         }
39463         var box = this.el.getBox();
39464         if (box.width == 0) {
39465             box.width = this.config.width; // kludge?
39466         }
39467         if(this.split){
39468             box.width += this.split.el.getWidth();
39469         }
39470         return box;
39471     },
39472     
39473     updateBox : function(box){
39474         if(this.split && !this.collapsed){
39475             var sw = this.split.el.getWidth();
39476             box.width -= sw;
39477             this.split.el.setLeft(box.x+box.width);
39478             this.split.el.setTop(box.y);
39479             this.split.el.setHeight(box.height);
39480         }
39481         if(this.collapsed){
39482             this.updateBody(null, box.height);
39483         }
39484         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39485     }
39486 });Roo.namespace("Roo.bootstrap.panel");/*
39487  * Based on:
39488  * Ext JS Library 1.1.1
39489  * Copyright(c) 2006-2007, Ext JS, LLC.
39490  *
39491  * Originally Released Under LGPL - original licence link has changed is not relivant.
39492  *
39493  * Fork - LGPL
39494  * <script type="text/javascript">
39495  */
39496 /**
39497  * @class Roo.ContentPanel
39498  * @extends Roo.util.Observable
39499  * A basic ContentPanel element.
39500  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39501  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39502  * @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
39503  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39504  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39505  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39506  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39507  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39508  * @cfg {String} title          The title for this panel
39509  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39510  * @cfg {String} url            Calls {@link #setUrl} with this value
39511  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39512  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39513  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39514  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39515  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39516  * @cfg {Boolean} badges render the badges
39517  * @cfg {String} cls  extra classes to use  
39518  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39519
39520  * @constructor
39521  * Create a new ContentPanel.
39522  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39523  * @param {String/Object} config A string to set only the title or a config object
39524  * @param {String} content (optional) Set the HTML content for this panel
39525  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39526  */
39527 Roo.bootstrap.panel.Content = function( config){
39528     
39529     this.tpl = config.tpl || false;
39530     
39531     var el = config.el;
39532     var content = config.content;
39533
39534     if(config.autoCreate){ // xtype is available if this is called from factory
39535         el = Roo.id();
39536     }
39537     this.el = Roo.get(el);
39538     if(!this.el && config && config.autoCreate){
39539         if(typeof config.autoCreate == "object"){
39540             if(!config.autoCreate.id){
39541                 config.autoCreate.id = config.id||el;
39542             }
39543             this.el = Roo.DomHelper.append(document.body,
39544                         config.autoCreate, true);
39545         }else{
39546             var elcfg =  {
39547                 tag: "div",
39548                 cls: (config.cls || '') +
39549                     (config.background ? ' bg-' + config.background : '') +
39550                     " roo-layout-inactive-content",
39551                 id: config.id||el
39552             };
39553             if (config.iframe) {
39554                 elcfg.cn = [
39555                     {
39556                         tag : 'iframe',
39557                         style : 'border: 0px',
39558                         src : 'about:blank'
39559                     }
39560                 ];
39561             }
39562               
39563             if (config.html) {
39564                 elcfg.html = config.html;
39565                 
39566             }
39567                         
39568             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39569             if (config.iframe) {
39570                 this.iframeEl = this.el.select('iframe',true).first();
39571             }
39572             
39573         }
39574     } 
39575     this.closable = false;
39576     this.loaded = false;
39577     this.active = false;
39578    
39579       
39580     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39581         
39582         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39583         
39584         this.wrapEl = this.el; //this.el.wrap();
39585         var ti = [];
39586         if (config.toolbar.items) {
39587             ti = config.toolbar.items ;
39588             delete config.toolbar.items ;
39589         }
39590         
39591         var nitems = [];
39592         this.toolbar.render(this.wrapEl, 'before');
39593         for(var i =0;i < ti.length;i++) {
39594           //  Roo.log(['add child', items[i]]);
39595             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39596         }
39597         this.toolbar.items = nitems;
39598         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39599         delete config.toolbar;
39600         
39601     }
39602     /*
39603     // xtype created footer. - not sure if will work as we normally have to render first..
39604     if (this.footer && !this.footer.el && this.footer.xtype) {
39605         if (!this.wrapEl) {
39606             this.wrapEl = this.el.wrap();
39607         }
39608     
39609         this.footer.container = this.wrapEl.createChild();
39610          
39611         this.footer = Roo.factory(this.footer, Roo);
39612         
39613     }
39614     */
39615     
39616      if(typeof config == "string"){
39617         this.title = config;
39618     }else{
39619         Roo.apply(this, config);
39620     }
39621     
39622     if(this.resizeEl){
39623         this.resizeEl = Roo.get(this.resizeEl, true);
39624     }else{
39625         this.resizeEl = this.el;
39626     }
39627     // handle view.xtype
39628     
39629  
39630     
39631     
39632     this.addEvents({
39633         /**
39634          * @event activate
39635          * Fires when this panel is activated. 
39636          * @param {Roo.ContentPanel} this
39637          */
39638         "activate" : true,
39639         /**
39640          * @event deactivate
39641          * Fires when this panel is activated. 
39642          * @param {Roo.ContentPanel} this
39643          */
39644         "deactivate" : true,
39645
39646         /**
39647          * @event resize
39648          * Fires when this panel is resized if fitToFrame is true.
39649          * @param {Roo.ContentPanel} this
39650          * @param {Number} width The width after any component adjustments
39651          * @param {Number} height The height after any component adjustments
39652          */
39653         "resize" : true,
39654         
39655          /**
39656          * @event render
39657          * Fires when this tab is created
39658          * @param {Roo.ContentPanel} this
39659          */
39660         "render" : true
39661         
39662         
39663         
39664     });
39665     
39666
39667     
39668     
39669     if(this.autoScroll && !this.iframe){
39670         this.resizeEl.setStyle("overflow", "auto");
39671     } else {
39672         // fix randome scrolling
39673         //this.el.on('scroll', function() {
39674         //    Roo.log('fix random scolling');
39675         //    this.scrollTo('top',0); 
39676         //});
39677     }
39678     content = content || this.content;
39679     if(content){
39680         this.setContent(content);
39681     }
39682     if(config && config.url){
39683         this.setUrl(this.url, this.params, this.loadOnce);
39684     }
39685     
39686     
39687     
39688     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39689     
39690     if (this.view && typeof(this.view.xtype) != 'undefined') {
39691         this.view.el = this.el.appendChild(document.createElement("div"));
39692         this.view = Roo.factory(this.view); 
39693         this.view.render  &&  this.view.render(false, '');  
39694     }
39695     
39696     
39697     this.fireEvent('render', this);
39698 };
39699
39700 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39701     
39702     cls : '',
39703     background : '',
39704     
39705     tabTip : '',
39706     
39707     iframe : false,
39708     iframeEl : false,
39709     
39710     setRegion : function(region){
39711         this.region = region;
39712         this.setActiveClass(region && !this.background);
39713     },
39714     
39715     
39716     setActiveClass: function(state)
39717     {
39718         if(state){
39719            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39720            this.el.setStyle('position','relative');
39721         }else{
39722            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39723            this.el.setStyle('position', 'absolute');
39724         } 
39725     },
39726     
39727     /**
39728      * Returns the toolbar for this Panel if one was configured. 
39729      * @return {Roo.Toolbar} 
39730      */
39731     getToolbar : function(){
39732         return this.toolbar;
39733     },
39734     
39735     setActiveState : function(active)
39736     {
39737         this.active = active;
39738         this.setActiveClass(active);
39739         if(!active){
39740             if(this.fireEvent("deactivate", this) === false){
39741                 return false;
39742             }
39743             return true;
39744         }
39745         this.fireEvent("activate", this);
39746         return true;
39747     },
39748     /**
39749      * Updates this panel's element (not for iframe)
39750      * @param {String} content The new content
39751      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39752     */
39753     setContent : function(content, loadScripts){
39754         if (this.iframe) {
39755             return;
39756         }
39757         
39758         this.el.update(content, loadScripts);
39759     },
39760
39761     ignoreResize : function(w, h){
39762         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39763             return true;
39764         }else{
39765             this.lastSize = {width: w, height: h};
39766             return false;
39767         }
39768     },
39769     /**
39770      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39771      * @return {Roo.UpdateManager} The UpdateManager
39772      */
39773     getUpdateManager : function(){
39774         if (this.iframe) {
39775             return false;
39776         }
39777         return this.el.getUpdateManager();
39778     },
39779      /**
39780      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39781      * Does not work with IFRAME contents
39782      * @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:
39783 <pre><code>
39784 panel.load({
39785     url: "your-url.php",
39786     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39787     callback: yourFunction,
39788     scope: yourObject, //(optional scope)
39789     discardUrl: false,
39790     nocache: false,
39791     text: "Loading...",
39792     timeout: 30,
39793     scripts: false
39794 });
39795 </code></pre>
39796      
39797      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39798      * 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.
39799      * @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}
39800      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39801      * @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.
39802      * @return {Roo.ContentPanel} this
39803      */
39804     load : function(){
39805         
39806         if (this.iframe) {
39807             return this;
39808         }
39809         
39810         var um = this.el.getUpdateManager();
39811         um.update.apply(um, arguments);
39812         return this;
39813     },
39814
39815
39816     /**
39817      * 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.
39818      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39819      * @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)
39820      * @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)
39821      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39822      */
39823     setUrl : function(url, params, loadOnce){
39824         if (this.iframe) {
39825             this.iframeEl.dom.src = url;
39826             return false;
39827         }
39828         
39829         if(this.refreshDelegate){
39830             this.removeListener("activate", this.refreshDelegate);
39831         }
39832         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39833         this.on("activate", this.refreshDelegate);
39834         return this.el.getUpdateManager();
39835     },
39836     
39837     _handleRefresh : function(url, params, loadOnce){
39838         if(!loadOnce || !this.loaded){
39839             var updater = this.el.getUpdateManager();
39840             updater.update(url, params, this._setLoaded.createDelegate(this));
39841         }
39842     },
39843     
39844     _setLoaded : function(){
39845         this.loaded = true;
39846     }, 
39847     
39848     /**
39849      * Returns this panel's id
39850      * @return {String} 
39851      */
39852     getId : function(){
39853         return this.el.id;
39854     },
39855     
39856     /** 
39857      * Returns this panel's element - used by regiosn to add.
39858      * @return {Roo.Element} 
39859      */
39860     getEl : function(){
39861         return this.wrapEl || this.el;
39862     },
39863     
39864    
39865     
39866     adjustForComponents : function(width, height)
39867     {
39868         //Roo.log('adjustForComponents ');
39869         if(this.resizeEl != this.el){
39870             width -= this.el.getFrameWidth('lr');
39871             height -= this.el.getFrameWidth('tb');
39872         }
39873         if(this.toolbar){
39874             var te = this.toolbar.getEl();
39875             te.setWidth(width);
39876             height -= te.getHeight();
39877         }
39878         if(this.footer){
39879             var te = this.footer.getEl();
39880             te.setWidth(width);
39881             height -= te.getHeight();
39882         }
39883         
39884         
39885         if(this.adjustments){
39886             width += this.adjustments[0];
39887             height += this.adjustments[1];
39888         }
39889         return {"width": width, "height": height};
39890     },
39891     
39892     setSize : function(width, height){
39893         if(this.fitToFrame && !this.ignoreResize(width, height)){
39894             if(this.fitContainer && this.resizeEl != this.el){
39895                 this.el.setSize(width, height);
39896             }
39897             var size = this.adjustForComponents(width, height);
39898             if (this.iframe) {
39899                 this.iframeEl.setSize(width,height);
39900             }
39901             
39902             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39903             this.fireEvent('resize', this, size.width, size.height);
39904             
39905             
39906         }
39907     },
39908     
39909     /**
39910      * Returns this panel's title
39911      * @return {String} 
39912      */
39913     getTitle : function(){
39914         
39915         if (typeof(this.title) != 'object') {
39916             return this.title;
39917         }
39918         
39919         var t = '';
39920         for (var k in this.title) {
39921             if (!this.title.hasOwnProperty(k)) {
39922                 continue;
39923             }
39924             
39925             if (k.indexOf('-') >= 0) {
39926                 var s = k.split('-');
39927                 for (var i = 0; i<s.length; i++) {
39928                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39929                 }
39930             } else {
39931                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39932             }
39933         }
39934         return t;
39935     },
39936     
39937     /**
39938      * Set this panel's title
39939      * @param {String} title
39940      */
39941     setTitle : function(title){
39942         this.title = title;
39943         if(this.region){
39944             this.region.updatePanelTitle(this, title);
39945         }
39946     },
39947     
39948     /**
39949      * Returns true is this panel was configured to be closable
39950      * @return {Boolean} 
39951      */
39952     isClosable : function(){
39953         return this.closable;
39954     },
39955     
39956     beforeSlide : function(){
39957         this.el.clip();
39958         this.resizeEl.clip();
39959     },
39960     
39961     afterSlide : function(){
39962         this.el.unclip();
39963         this.resizeEl.unclip();
39964     },
39965     
39966     /**
39967      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39968      *   Will fail silently if the {@link #setUrl} method has not been called.
39969      *   This does not activate the panel, just updates its content.
39970      */
39971     refresh : function(){
39972         if(this.refreshDelegate){
39973            this.loaded = false;
39974            this.refreshDelegate();
39975         }
39976     },
39977     
39978     /**
39979      * Destroys this panel
39980      */
39981     destroy : function(){
39982         this.el.removeAllListeners();
39983         var tempEl = document.createElement("span");
39984         tempEl.appendChild(this.el.dom);
39985         tempEl.innerHTML = "";
39986         this.el.remove();
39987         this.el = null;
39988     },
39989     
39990     /**
39991      * form - if the content panel contains a form - this is a reference to it.
39992      * @type {Roo.form.Form}
39993      */
39994     form : false,
39995     /**
39996      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39997      *    This contains a reference to it.
39998      * @type {Roo.View}
39999      */
40000     view : false,
40001     
40002       /**
40003      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40004      * <pre><code>
40005
40006 layout.addxtype({
40007        xtype : 'Form',
40008        items: [ .... ]
40009    }
40010 );
40011
40012 </code></pre>
40013      * @param {Object} cfg Xtype definition of item to add.
40014      */
40015     
40016     
40017     getChildContainer: function () {
40018         return this.getEl();
40019     }
40020     
40021     
40022     /*
40023         var  ret = new Roo.factory(cfg);
40024         return ret;
40025         
40026         
40027         // add form..
40028         if (cfg.xtype.match(/^Form$/)) {
40029             
40030             var el;
40031             //if (this.footer) {
40032             //    el = this.footer.container.insertSibling(false, 'before');
40033             //} else {
40034                 el = this.el.createChild();
40035             //}
40036
40037             this.form = new  Roo.form.Form(cfg);
40038             
40039             
40040             if ( this.form.allItems.length) {
40041                 this.form.render(el.dom);
40042             }
40043             return this.form;
40044         }
40045         // should only have one of theses..
40046         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40047             // views.. should not be just added - used named prop 'view''
40048             
40049             cfg.el = this.el.appendChild(document.createElement("div"));
40050             // factory?
40051             
40052             var ret = new Roo.factory(cfg);
40053              
40054              ret.render && ret.render(false, ''); // render blank..
40055             this.view = ret;
40056             return ret;
40057         }
40058         return false;
40059     }
40060     \*/
40061 });
40062  
40063 /**
40064  * @class Roo.bootstrap.panel.Grid
40065  * @extends Roo.bootstrap.panel.Content
40066  * @constructor
40067  * Create a new GridPanel.
40068  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40069  * @param {Object} config A the config object
40070   
40071  */
40072
40073
40074
40075 Roo.bootstrap.panel.Grid = function(config)
40076 {
40077     
40078       
40079     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40080         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40081
40082     config.el = this.wrapper;
40083     //this.el = this.wrapper;
40084     
40085       if (config.container) {
40086         // ctor'ed from a Border/panel.grid
40087         
40088         
40089         this.wrapper.setStyle("overflow", "hidden");
40090         this.wrapper.addClass('roo-grid-container');
40091
40092     }
40093     
40094     
40095     if(config.toolbar){
40096         var tool_el = this.wrapper.createChild();    
40097         this.toolbar = Roo.factory(config.toolbar);
40098         var ti = [];
40099         if (config.toolbar.items) {
40100             ti = config.toolbar.items ;
40101             delete config.toolbar.items ;
40102         }
40103         
40104         var nitems = [];
40105         this.toolbar.render(tool_el);
40106         for(var i =0;i < ti.length;i++) {
40107           //  Roo.log(['add child', items[i]]);
40108             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40109         }
40110         this.toolbar.items = nitems;
40111         
40112         delete config.toolbar;
40113     }
40114     
40115     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40116     config.grid.scrollBody = true;;
40117     config.grid.monitorWindowResize = false; // turn off autosizing
40118     config.grid.autoHeight = false;
40119     config.grid.autoWidth = false;
40120     
40121     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40122     
40123     if (config.background) {
40124         // render grid on panel activation (if panel background)
40125         this.on('activate', function(gp) {
40126             if (!gp.grid.rendered) {
40127                 gp.grid.render(this.wrapper);
40128                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40129             }
40130         });
40131             
40132     } else {
40133         this.grid.render(this.wrapper);
40134         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40135
40136     }
40137     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40138     // ??? needed ??? config.el = this.wrapper;
40139     
40140     
40141     
40142   
40143     // xtype created footer. - not sure if will work as we normally have to render first..
40144     if (this.footer && !this.footer.el && this.footer.xtype) {
40145         
40146         var ctr = this.grid.getView().getFooterPanel(true);
40147         this.footer.dataSource = this.grid.dataSource;
40148         this.footer = Roo.factory(this.footer, Roo);
40149         this.footer.render(ctr);
40150         
40151     }
40152     
40153     
40154     
40155     
40156      
40157 };
40158
40159 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40160     getId : function(){
40161         return this.grid.id;
40162     },
40163     
40164     /**
40165      * Returns the grid for this panel
40166      * @return {Roo.bootstrap.Table} 
40167      */
40168     getGrid : function(){
40169         return this.grid;    
40170     },
40171     
40172     setSize : function(width, height){
40173         if(!this.ignoreResize(width, height)){
40174             var grid = this.grid;
40175             var size = this.adjustForComponents(width, height);
40176             // tfoot is not a footer?
40177           
40178             
40179             var gridel = grid.getGridEl();
40180             gridel.setSize(size.width, size.height);
40181             
40182             var tbd = grid.getGridEl().select('tbody', true).first();
40183             var thd = grid.getGridEl().select('thead',true).first();
40184             var tbf= grid.getGridEl().select('tfoot', true).first();
40185
40186             if (tbf) {
40187                 size.height -= tbf.getHeight();
40188             }
40189             if (thd) {
40190                 size.height -= thd.getHeight();
40191             }
40192             
40193             tbd.setSize(size.width, size.height );
40194             // this is for the account management tab -seems to work there.
40195             var thd = grid.getGridEl().select('thead',true).first();
40196             //if (tbd) {
40197             //    tbd.setSize(size.width, size.height - thd.getHeight());
40198             //}
40199              
40200             grid.autoSize();
40201         }
40202     },
40203      
40204     
40205     
40206     beforeSlide : function(){
40207         this.grid.getView().scroller.clip();
40208     },
40209     
40210     afterSlide : function(){
40211         this.grid.getView().scroller.unclip();
40212     },
40213     
40214     destroy : function(){
40215         this.grid.destroy();
40216         delete this.grid;
40217         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40218     }
40219 });
40220
40221 /**
40222  * @class Roo.bootstrap.panel.Nest
40223  * @extends Roo.bootstrap.panel.Content
40224  * @constructor
40225  * Create a new Panel, that can contain a layout.Border.
40226  * 
40227  * 
40228  * @param {Roo.BorderLayout} layout The layout for this panel
40229  * @param {String/Object} config A string to set only the title or a config object
40230  */
40231 Roo.bootstrap.panel.Nest = function(config)
40232 {
40233     // construct with only one argument..
40234     /* FIXME - implement nicer consturctors
40235     if (layout.layout) {
40236         config = layout;
40237         layout = config.layout;
40238         delete config.layout;
40239     }
40240     if (layout.xtype && !layout.getEl) {
40241         // then layout needs constructing..
40242         layout = Roo.factory(layout, Roo);
40243     }
40244     */
40245     
40246     config.el =  config.layout.getEl();
40247     
40248     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40249     
40250     config.layout.monitorWindowResize = false; // turn off autosizing
40251     this.layout = config.layout;
40252     this.layout.getEl().addClass("roo-layout-nested-layout");
40253     this.layout.parent = this;
40254     
40255     
40256     
40257     
40258 };
40259
40260 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40261
40262     setSize : function(width, height){
40263         if(!this.ignoreResize(width, height)){
40264             var size = this.adjustForComponents(width, height);
40265             var el = this.layout.getEl();
40266             if (size.height < 1) {
40267                 el.setWidth(size.width);   
40268             } else {
40269                 el.setSize(size.width, size.height);
40270             }
40271             var touch = el.dom.offsetWidth;
40272             this.layout.layout();
40273             // ie requires a double layout on the first pass
40274             if(Roo.isIE && !this.initialized){
40275                 this.initialized = true;
40276                 this.layout.layout();
40277             }
40278         }
40279     },
40280     
40281     // activate all subpanels if not currently active..
40282     
40283     setActiveState : function(active){
40284         this.active = active;
40285         this.setActiveClass(active);
40286         
40287         if(!active){
40288             this.fireEvent("deactivate", this);
40289             return;
40290         }
40291         
40292         this.fireEvent("activate", this);
40293         // not sure if this should happen before or after..
40294         if (!this.layout) {
40295             return; // should not happen..
40296         }
40297         var reg = false;
40298         for (var r in this.layout.regions) {
40299             reg = this.layout.getRegion(r);
40300             if (reg.getActivePanel()) {
40301                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40302                 reg.setActivePanel(reg.getActivePanel());
40303                 continue;
40304             }
40305             if (!reg.panels.length) {
40306                 continue;
40307             }
40308             reg.showPanel(reg.getPanel(0));
40309         }
40310         
40311         
40312         
40313         
40314     },
40315     
40316     /**
40317      * Returns the nested BorderLayout for this panel
40318      * @return {Roo.BorderLayout} 
40319      */
40320     getLayout : function(){
40321         return this.layout;
40322     },
40323     
40324      /**
40325      * Adds a xtype elements to the layout of the nested panel
40326      * <pre><code>
40327
40328 panel.addxtype({
40329        xtype : 'ContentPanel',
40330        region: 'west',
40331        items: [ .... ]
40332    }
40333 );
40334
40335 panel.addxtype({
40336         xtype : 'NestedLayoutPanel',
40337         region: 'west',
40338         layout: {
40339            center: { },
40340            west: { }   
40341         },
40342         items : [ ... list of content panels or nested layout panels.. ]
40343    }
40344 );
40345 </code></pre>
40346      * @param {Object} cfg Xtype definition of item to add.
40347      */
40348     addxtype : function(cfg) {
40349         return this.layout.addxtype(cfg);
40350     
40351     }
40352 });/*
40353  * Based on:
40354  * Ext JS Library 1.1.1
40355  * Copyright(c) 2006-2007, Ext JS, LLC.
40356  *
40357  * Originally Released Under LGPL - original licence link has changed is not relivant.
40358  *
40359  * Fork - LGPL
40360  * <script type="text/javascript">
40361  */
40362 /**
40363  * @class Roo.TabPanel
40364  * @extends Roo.util.Observable
40365  * A lightweight tab container.
40366  * <br><br>
40367  * Usage:
40368  * <pre><code>
40369 // basic tabs 1, built from existing content
40370 var tabs = new Roo.TabPanel("tabs1");
40371 tabs.addTab("script", "View Script");
40372 tabs.addTab("markup", "View Markup");
40373 tabs.activate("script");
40374
40375 // more advanced tabs, built from javascript
40376 var jtabs = new Roo.TabPanel("jtabs");
40377 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40378
40379 // set up the UpdateManager
40380 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40381 var updater = tab2.getUpdateManager();
40382 updater.setDefaultUrl("ajax1.htm");
40383 tab2.on('activate', updater.refresh, updater, true);
40384
40385 // Use setUrl for Ajax loading
40386 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40387 tab3.setUrl("ajax2.htm", null, true);
40388
40389 // Disabled tab
40390 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40391 tab4.disable();
40392
40393 jtabs.activate("jtabs-1");
40394  * </code></pre>
40395  * @constructor
40396  * Create a new TabPanel.
40397  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40398  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40399  */
40400 Roo.bootstrap.panel.Tabs = function(config){
40401     /**
40402     * The container element for this TabPanel.
40403     * @type Roo.Element
40404     */
40405     this.el = Roo.get(config.el);
40406     delete config.el;
40407     if(config){
40408         if(typeof config == "boolean"){
40409             this.tabPosition = config ? "bottom" : "top";
40410         }else{
40411             Roo.apply(this, config);
40412         }
40413     }
40414     
40415     if(this.tabPosition == "bottom"){
40416         // if tabs are at the bottom = create the body first.
40417         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40418         this.el.addClass("roo-tabs-bottom");
40419     }
40420     // next create the tabs holders
40421     
40422     if (this.tabPosition == "west"){
40423         
40424         var reg = this.region; // fake it..
40425         while (reg) {
40426             if (!reg.mgr.parent) {
40427                 break;
40428             }
40429             reg = reg.mgr.parent.region;
40430         }
40431         Roo.log("got nest?");
40432         Roo.log(reg);
40433         if (reg.mgr.getRegion('west')) {
40434             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40435             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40436             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40437             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40438             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40439         
40440             
40441         }
40442         
40443         
40444     } else {
40445      
40446         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40447         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40448         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40449         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40450     }
40451     
40452     
40453     if(Roo.isIE){
40454         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40455     }
40456     
40457     // finally - if tabs are at the top, then create the body last..
40458     if(this.tabPosition != "bottom"){
40459         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40460          * @type Roo.Element
40461          */
40462         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40463         this.el.addClass("roo-tabs-top");
40464     }
40465     this.items = [];
40466
40467     this.bodyEl.setStyle("position", "relative");
40468
40469     this.active = null;
40470     this.activateDelegate = this.activate.createDelegate(this);
40471
40472     this.addEvents({
40473         /**
40474          * @event tabchange
40475          * Fires when the active tab changes
40476          * @param {Roo.TabPanel} this
40477          * @param {Roo.TabPanelItem} activePanel The new active tab
40478          */
40479         "tabchange": true,
40480         /**
40481          * @event beforetabchange
40482          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40483          * @param {Roo.TabPanel} this
40484          * @param {Object} e Set cancel to true on this object to cancel the tab change
40485          * @param {Roo.TabPanelItem} tab The tab being changed to
40486          */
40487         "beforetabchange" : true
40488     });
40489
40490     Roo.EventManager.onWindowResize(this.onResize, this);
40491     this.cpad = this.el.getPadding("lr");
40492     this.hiddenCount = 0;
40493
40494
40495     // toolbar on the tabbar support...
40496     if (this.toolbar) {
40497         alert("no toolbar support yet");
40498         this.toolbar  = false;
40499         /*
40500         var tcfg = this.toolbar;
40501         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40502         this.toolbar = new Roo.Toolbar(tcfg);
40503         if (Roo.isSafari) {
40504             var tbl = tcfg.container.child('table', true);
40505             tbl.setAttribute('width', '100%');
40506         }
40507         */
40508         
40509     }
40510    
40511
40512
40513     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40514 };
40515
40516 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40517     /*
40518      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40519      */
40520     tabPosition : "top",
40521     /*
40522      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40523      */
40524     currentTabWidth : 0,
40525     /*
40526      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40527      */
40528     minTabWidth : 40,
40529     /*
40530      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40531      */
40532     maxTabWidth : 250,
40533     /*
40534      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40535      */
40536     preferredTabWidth : 175,
40537     /*
40538      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40539      */
40540     resizeTabs : false,
40541     /*
40542      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40543      */
40544     monitorResize : true,
40545     /*
40546      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40547      */
40548     toolbar : false,  // set by caller..
40549     
40550     region : false, /// set by caller
40551     
40552     disableTooltips : true, // not used yet...
40553
40554     /**
40555      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40556      * @param {String} id The id of the div to use <b>or create</b>
40557      * @param {String} text The text for the tab
40558      * @param {String} content (optional) Content to put in the TabPanelItem body
40559      * @param {Boolean} closable (optional) True to create a close icon on the tab
40560      * @return {Roo.TabPanelItem} The created TabPanelItem
40561      */
40562     addTab : function(id, text, content, closable, tpl)
40563     {
40564         var item = new Roo.bootstrap.panel.TabItem({
40565             panel: this,
40566             id : id,
40567             text : text,
40568             closable : closable,
40569             tpl : tpl
40570         });
40571         this.addTabItem(item);
40572         if(content){
40573             item.setContent(content);
40574         }
40575         return item;
40576     },
40577
40578     /**
40579      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40580      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40581      * @return {Roo.TabPanelItem}
40582      */
40583     getTab : function(id){
40584         return this.items[id];
40585     },
40586
40587     /**
40588      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40589      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40590      */
40591     hideTab : function(id){
40592         var t = this.items[id];
40593         if(!t.isHidden()){
40594            t.setHidden(true);
40595            this.hiddenCount++;
40596            this.autoSizeTabs();
40597         }
40598     },
40599
40600     /**
40601      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40602      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40603      */
40604     unhideTab : function(id){
40605         var t = this.items[id];
40606         if(t.isHidden()){
40607            t.setHidden(false);
40608            this.hiddenCount--;
40609            this.autoSizeTabs();
40610         }
40611     },
40612
40613     /**
40614      * Adds an existing {@link Roo.TabPanelItem}.
40615      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40616      */
40617     addTabItem : function(item)
40618     {
40619         this.items[item.id] = item;
40620         this.items.push(item);
40621         this.autoSizeTabs();
40622       //  if(this.resizeTabs){
40623     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40624   //         this.autoSizeTabs();
40625 //        }else{
40626 //            item.autoSize();
40627        // }
40628     },
40629
40630     /**
40631      * Removes a {@link Roo.TabPanelItem}.
40632      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40633      */
40634     removeTab : function(id){
40635         var items = this.items;
40636         var tab = items[id];
40637         if(!tab) { return; }
40638         var index = items.indexOf(tab);
40639         if(this.active == tab && items.length > 1){
40640             var newTab = this.getNextAvailable(index);
40641             if(newTab) {
40642                 newTab.activate();
40643             }
40644         }
40645         this.stripEl.dom.removeChild(tab.pnode.dom);
40646         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40647             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40648         }
40649         items.splice(index, 1);
40650         delete this.items[tab.id];
40651         tab.fireEvent("close", tab);
40652         tab.purgeListeners();
40653         this.autoSizeTabs();
40654     },
40655
40656     getNextAvailable : function(start){
40657         var items = this.items;
40658         var index = start;
40659         // look for a next tab that will slide over to
40660         // replace the one being removed
40661         while(index < items.length){
40662             var item = items[++index];
40663             if(item && !item.isHidden()){
40664                 return item;
40665             }
40666         }
40667         // if one isn't found select the previous tab (on the left)
40668         index = start;
40669         while(index >= 0){
40670             var item = items[--index];
40671             if(item && !item.isHidden()){
40672                 return item;
40673             }
40674         }
40675         return null;
40676     },
40677
40678     /**
40679      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40680      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40681      */
40682     disableTab : function(id){
40683         var tab = this.items[id];
40684         if(tab && this.active != tab){
40685             tab.disable();
40686         }
40687     },
40688
40689     /**
40690      * Enables a {@link Roo.TabPanelItem} that is disabled.
40691      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40692      */
40693     enableTab : function(id){
40694         var tab = this.items[id];
40695         tab.enable();
40696     },
40697
40698     /**
40699      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40700      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40701      * @return {Roo.TabPanelItem} The TabPanelItem.
40702      */
40703     activate : function(id)
40704     {
40705         //Roo.log('activite:'  + id);
40706         
40707         var tab = this.items[id];
40708         if(!tab){
40709             return null;
40710         }
40711         if(tab == this.active || tab.disabled){
40712             return tab;
40713         }
40714         var e = {};
40715         this.fireEvent("beforetabchange", this, e, tab);
40716         if(e.cancel !== true && !tab.disabled){
40717             if(this.active){
40718                 this.active.hide();
40719             }
40720             this.active = this.items[id];
40721             this.active.show();
40722             this.fireEvent("tabchange", this, this.active);
40723         }
40724         return tab;
40725     },
40726
40727     /**
40728      * Gets the active {@link Roo.TabPanelItem}.
40729      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40730      */
40731     getActiveTab : function(){
40732         return this.active;
40733     },
40734
40735     /**
40736      * Updates the tab body element to fit the height of the container element
40737      * for overflow scrolling
40738      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40739      */
40740     syncHeight : function(targetHeight){
40741         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40742         var bm = this.bodyEl.getMargins();
40743         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40744         this.bodyEl.setHeight(newHeight);
40745         return newHeight;
40746     },
40747
40748     onResize : function(){
40749         if(this.monitorResize){
40750             this.autoSizeTabs();
40751         }
40752     },
40753
40754     /**
40755      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40756      */
40757     beginUpdate : function(){
40758         this.updating = true;
40759     },
40760
40761     /**
40762      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40763      */
40764     endUpdate : function(){
40765         this.updating = false;
40766         this.autoSizeTabs();
40767     },
40768
40769     /**
40770      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40771      */
40772     autoSizeTabs : function()
40773     {
40774         var count = this.items.length;
40775         var vcount = count - this.hiddenCount;
40776         
40777         if (vcount < 2) {
40778             this.stripEl.hide();
40779         } else {
40780             this.stripEl.show();
40781         }
40782         
40783         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40784             return;
40785         }
40786         
40787         
40788         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40789         var availWidth = Math.floor(w / vcount);
40790         var b = this.stripBody;
40791         if(b.getWidth() > w){
40792             var tabs = this.items;
40793             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40794             if(availWidth < this.minTabWidth){
40795                 /*if(!this.sleft){    // incomplete scrolling code
40796                     this.createScrollButtons();
40797                 }
40798                 this.showScroll();
40799                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40800             }
40801         }else{
40802             if(this.currentTabWidth < this.preferredTabWidth){
40803                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40804             }
40805         }
40806     },
40807
40808     /**
40809      * Returns the number of tabs in this TabPanel.
40810      * @return {Number}
40811      */
40812      getCount : function(){
40813          return this.items.length;
40814      },
40815
40816     /**
40817      * Resizes all the tabs to the passed width
40818      * @param {Number} The new width
40819      */
40820     setTabWidth : function(width){
40821         this.currentTabWidth = width;
40822         for(var i = 0, len = this.items.length; i < len; i++) {
40823                 if(!this.items[i].isHidden()) {
40824                 this.items[i].setWidth(width);
40825             }
40826         }
40827     },
40828
40829     /**
40830      * Destroys this TabPanel
40831      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40832      */
40833     destroy : function(removeEl){
40834         Roo.EventManager.removeResizeListener(this.onResize, this);
40835         for(var i = 0, len = this.items.length; i < len; i++){
40836             this.items[i].purgeListeners();
40837         }
40838         if(removeEl === true){
40839             this.el.update("");
40840             this.el.remove();
40841         }
40842     },
40843     
40844     createStrip : function(container)
40845     {
40846         var strip = document.createElement("nav");
40847         strip.className = Roo.bootstrap.version == 4 ?
40848             "navbar-light bg-light" : 
40849             "navbar navbar-default"; //"x-tabs-wrap";
40850         container.appendChild(strip);
40851         return strip;
40852     },
40853     
40854     createStripList : function(strip)
40855     {
40856         // div wrapper for retard IE
40857         // returns the "tr" element.
40858         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40859         //'<div class="x-tabs-strip-wrap">'+
40860           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40861           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40862         return strip.firstChild; //.firstChild.firstChild.firstChild;
40863     },
40864     createBody : function(container)
40865     {
40866         var body = document.createElement("div");
40867         Roo.id(body, "tab-body");
40868         //Roo.fly(body).addClass("x-tabs-body");
40869         Roo.fly(body).addClass("tab-content");
40870         container.appendChild(body);
40871         return body;
40872     },
40873     createItemBody :function(bodyEl, id){
40874         var body = Roo.getDom(id);
40875         if(!body){
40876             body = document.createElement("div");
40877             body.id = id;
40878         }
40879         //Roo.fly(body).addClass("x-tabs-item-body");
40880         Roo.fly(body).addClass("tab-pane");
40881          bodyEl.insertBefore(body, bodyEl.firstChild);
40882         return body;
40883     },
40884     /** @private */
40885     createStripElements :  function(stripEl, text, closable, tpl)
40886     {
40887         var td = document.createElement("li"); // was td..
40888         td.className = 'nav-item';
40889         
40890         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40891         
40892         
40893         stripEl.appendChild(td);
40894         /*if(closable){
40895             td.className = "x-tabs-closable";
40896             if(!this.closeTpl){
40897                 this.closeTpl = new Roo.Template(
40898                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40899                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40900                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40901                 );
40902             }
40903             var el = this.closeTpl.overwrite(td, {"text": text});
40904             var close = el.getElementsByTagName("div")[0];
40905             var inner = el.getElementsByTagName("em")[0];
40906             return {"el": el, "close": close, "inner": inner};
40907         } else {
40908         */
40909         // not sure what this is..
40910 //            if(!this.tabTpl){
40911                 //this.tabTpl = new Roo.Template(
40912                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40913                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40914                 //);
40915 //                this.tabTpl = new Roo.Template(
40916 //                   '<a href="#">' +
40917 //                   '<span unselectable="on"' +
40918 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40919 //                            ' >{text}</span></a>'
40920 //                );
40921 //                
40922 //            }
40923
40924
40925             var template = tpl || this.tabTpl || false;
40926             
40927             if(!template){
40928                 template =  new Roo.Template(
40929                         Roo.bootstrap.version == 4 ? 
40930                             (
40931                                 '<a class="nav-link" href="#" unselectable="on"' +
40932                                      (this.disableTooltips ? '' : ' title="{text}"') +
40933                                      ' >{text}</a>'
40934                             ) : (
40935                                 '<a class="nav-link" href="#">' +
40936                                 '<span unselectable="on"' +
40937                                          (this.disableTooltips ? '' : ' title="{text}"') +
40938                                     ' >{text}</span></a>'
40939                             )
40940                 );
40941             }
40942             
40943             switch (typeof(template)) {
40944                 case 'object' :
40945                     break;
40946                 case 'string' :
40947                     template = new Roo.Template(template);
40948                     break;
40949                 default :
40950                     break;
40951             }
40952             
40953             var el = template.overwrite(td, {"text": text});
40954             
40955             var inner = el.getElementsByTagName("span")[0];
40956             
40957             return {"el": el, "inner": inner};
40958             
40959     }
40960         
40961     
40962 });
40963
40964 /**
40965  * @class Roo.TabPanelItem
40966  * @extends Roo.util.Observable
40967  * Represents an individual item (tab plus body) in a TabPanel.
40968  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40969  * @param {String} id The id of this TabPanelItem
40970  * @param {String} text The text for the tab of this TabPanelItem
40971  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40972  */
40973 Roo.bootstrap.panel.TabItem = function(config){
40974     /**
40975      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40976      * @type Roo.TabPanel
40977      */
40978     this.tabPanel = config.panel;
40979     /**
40980      * The id for this TabPanelItem
40981      * @type String
40982      */
40983     this.id = config.id;
40984     /** @private */
40985     this.disabled = false;
40986     /** @private */
40987     this.text = config.text;
40988     /** @private */
40989     this.loaded = false;
40990     this.closable = config.closable;
40991
40992     /**
40993      * The body element for this TabPanelItem.
40994      * @type Roo.Element
40995      */
40996     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40997     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40998     this.bodyEl.setStyle("display", "block");
40999     this.bodyEl.setStyle("zoom", "1");
41000     //this.hideAction();
41001
41002     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41003     /** @private */
41004     this.el = Roo.get(els.el);
41005     this.inner = Roo.get(els.inner, true);
41006      this.textEl = Roo.bootstrap.version == 4 ?
41007         this.el : Roo.get(this.el.dom.firstChild, true);
41008
41009     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41010     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41011
41012     
41013 //    this.el.on("mousedown", this.onTabMouseDown, this);
41014     this.el.on("click", this.onTabClick, this);
41015     /** @private */
41016     if(config.closable){
41017         var c = Roo.get(els.close, true);
41018         c.dom.title = this.closeText;
41019         c.addClassOnOver("close-over");
41020         c.on("click", this.closeClick, this);
41021      }
41022
41023     this.addEvents({
41024          /**
41025          * @event activate
41026          * Fires when this tab becomes the active tab.
41027          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41028          * @param {Roo.TabPanelItem} this
41029          */
41030         "activate": true,
41031         /**
41032          * @event beforeclose
41033          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41034          * @param {Roo.TabPanelItem} this
41035          * @param {Object} e Set cancel to true on this object to cancel the close.
41036          */
41037         "beforeclose": true,
41038         /**
41039          * @event close
41040          * Fires when this tab is closed.
41041          * @param {Roo.TabPanelItem} this
41042          */
41043          "close": true,
41044         /**
41045          * @event deactivate
41046          * Fires when this tab is no longer the active tab.
41047          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41048          * @param {Roo.TabPanelItem} this
41049          */
41050          "deactivate" : true
41051     });
41052     this.hidden = false;
41053
41054     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41055 };
41056
41057 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41058            {
41059     purgeListeners : function(){
41060        Roo.util.Observable.prototype.purgeListeners.call(this);
41061        this.el.removeAllListeners();
41062     },
41063     /**
41064      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41065      */
41066     show : function(){
41067         this.status_node.addClass("active");
41068         this.showAction();
41069         if(Roo.isOpera){
41070             this.tabPanel.stripWrap.repaint();
41071         }
41072         this.fireEvent("activate", this.tabPanel, this);
41073     },
41074
41075     /**
41076      * Returns true if this tab is the active tab.
41077      * @return {Boolean}
41078      */
41079     isActive : function(){
41080         return this.tabPanel.getActiveTab() == this;
41081     },
41082
41083     /**
41084      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41085      */
41086     hide : function(){
41087         this.status_node.removeClass("active");
41088         this.hideAction();
41089         this.fireEvent("deactivate", this.tabPanel, this);
41090     },
41091
41092     hideAction : function(){
41093         this.bodyEl.hide();
41094         this.bodyEl.setStyle("position", "absolute");
41095         this.bodyEl.setLeft("-20000px");
41096         this.bodyEl.setTop("-20000px");
41097     },
41098
41099     showAction : function(){
41100         this.bodyEl.setStyle("position", "relative");
41101         this.bodyEl.setTop("");
41102         this.bodyEl.setLeft("");
41103         this.bodyEl.show();
41104     },
41105
41106     /**
41107      * Set the tooltip for the tab.
41108      * @param {String} tooltip The tab's tooltip
41109      */
41110     setTooltip : function(text){
41111         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41112             this.textEl.dom.qtip = text;
41113             this.textEl.dom.removeAttribute('title');
41114         }else{
41115             this.textEl.dom.title = text;
41116         }
41117     },
41118
41119     onTabClick : function(e){
41120         e.preventDefault();
41121         this.tabPanel.activate(this.id);
41122     },
41123
41124     onTabMouseDown : function(e){
41125         e.preventDefault();
41126         this.tabPanel.activate(this.id);
41127     },
41128 /*
41129     getWidth : function(){
41130         return this.inner.getWidth();
41131     },
41132
41133     setWidth : function(width){
41134         var iwidth = width - this.linode.getPadding("lr");
41135         this.inner.setWidth(iwidth);
41136         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41137         this.linode.setWidth(width);
41138     },
41139 */
41140     /**
41141      * Show or hide the tab
41142      * @param {Boolean} hidden True to hide or false to show.
41143      */
41144     setHidden : function(hidden){
41145         this.hidden = hidden;
41146         this.linode.setStyle("display", hidden ? "none" : "");
41147     },
41148
41149     /**
41150      * Returns true if this tab is "hidden"
41151      * @return {Boolean}
41152      */
41153     isHidden : function(){
41154         return this.hidden;
41155     },
41156
41157     /**
41158      * Returns the text for this tab
41159      * @return {String}
41160      */
41161     getText : function(){
41162         return this.text;
41163     },
41164     /*
41165     autoSize : function(){
41166         //this.el.beginMeasure();
41167         this.textEl.setWidth(1);
41168         /*
41169          *  #2804 [new] Tabs in Roojs
41170          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41171          */
41172         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41173         //this.el.endMeasure();
41174     //},
41175
41176     /**
41177      * Sets the text for the tab (Note: this also sets the tooltip text)
41178      * @param {String} text The tab's text and tooltip
41179      */
41180     setText : function(text){
41181         this.text = text;
41182         this.textEl.update(text);
41183         this.setTooltip(text);
41184         //if(!this.tabPanel.resizeTabs){
41185         //    this.autoSize();
41186         //}
41187     },
41188     /**
41189      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41190      */
41191     activate : function(){
41192         this.tabPanel.activate(this.id);
41193     },
41194
41195     /**
41196      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41197      */
41198     disable : function(){
41199         if(this.tabPanel.active != this){
41200             this.disabled = true;
41201             this.status_node.addClass("disabled");
41202         }
41203     },
41204
41205     /**
41206      * Enables this TabPanelItem if it was previously disabled.
41207      */
41208     enable : function(){
41209         this.disabled = false;
41210         this.status_node.removeClass("disabled");
41211     },
41212
41213     /**
41214      * Sets the content for this TabPanelItem.
41215      * @param {String} content The content
41216      * @param {Boolean} loadScripts true to look for and load scripts
41217      */
41218     setContent : function(content, loadScripts){
41219         this.bodyEl.update(content, loadScripts);
41220     },
41221
41222     /**
41223      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41224      * @return {Roo.UpdateManager} The UpdateManager
41225      */
41226     getUpdateManager : function(){
41227         return this.bodyEl.getUpdateManager();
41228     },
41229
41230     /**
41231      * Set a URL to be used to load the content for this TabPanelItem.
41232      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41233      * @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)
41234      * @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)
41235      * @return {Roo.UpdateManager} The UpdateManager
41236      */
41237     setUrl : function(url, params, loadOnce){
41238         if(this.refreshDelegate){
41239             this.un('activate', this.refreshDelegate);
41240         }
41241         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41242         this.on("activate", this.refreshDelegate);
41243         return this.bodyEl.getUpdateManager();
41244     },
41245
41246     /** @private */
41247     _handleRefresh : function(url, params, loadOnce){
41248         if(!loadOnce || !this.loaded){
41249             var updater = this.bodyEl.getUpdateManager();
41250             updater.update(url, params, this._setLoaded.createDelegate(this));
41251         }
41252     },
41253
41254     /**
41255      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41256      *   Will fail silently if the setUrl method has not been called.
41257      *   This does not activate the panel, just updates its content.
41258      */
41259     refresh : function(){
41260         if(this.refreshDelegate){
41261            this.loaded = false;
41262            this.refreshDelegate();
41263         }
41264     },
41265
41266     /** @private */
41267     _setLoaded : function(){
41268         this.loaded = true;
41269     },
41270
41271     /** @private */
41272     closeClick : function(e){
41273         var o = {};
41274         e.stopEvent();
41275         this.fireEvent("beforeclose", this, o);
41276         if(o.cancel !== true){
41277             this.tabPanel.removeTab(this.id);
41278         }
41279     },
41280     /**
41281      * The text displayed in the tooltip for the close icon.
41282      * @type String
41283      */
41284     closeText : "Close this tab"
41285 });
41286 /**
41287 *    This script refer to:
41288 *    Title: International Telephone Input
41289 *    Author: Jack O'Connor
41290 *    Code version:  v12.1.12
41291 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41292 **/
41293
41294 Roo.bootstrap.PhoneInputData = function() {
41295     var d = [
41296       [
41297         "Afghanistan (‫افغانستان‬‎)",
41298         "af",
41299         "93"
41300       ],
41301       [
41302         "Albania (Shqipëri)",
41303         "al",
41304         "355"
41305       ],
41306       [
41307         "Algeria (‫الجزائر‬‎)",
41308         "dz",
41309         "213"
41310       ],
41311       [
41312         "American Samoa",
41313         "as",
41314         "1684"
41315       ],
41316       [
41317         "Andorra",
41318         "ad",
41319         "376"
41320       ],
41321       [
41322         "Angola",
41323         "ao",
41324         "244"
41325       ],
41326       [
41327         "Anguilla",
41328         "ai",
41329         "1264"
41330       ],
41331       [
41332         "Antigua and Barbuda",
41333         "ag",
41334         "1268"
41335       ],
41336       [
41337         "Argentina",
41338         "ar",
41339         "54"
41340       ],
41341       [
41342         "Armenia (Հայաստան)",
41343         "am",
41344         "374"
41345       ],
41346       [
41347         "Aruba",
41348         "aw",
41349         "297"
41350       ],
41351       [
41352         "Australia",
41353         "au",
41354         "61",
41355         0
41356       ],
41357       [
41358         "Austria (Österreich)",
41359         "at",
41360         "43"
41361       ],
41362       [
41363         "Azerbaijan (Azərbaycan)",
41364         "az",
41365         "994"
41366       ],
41367       [
41368         "Bahamas",
41369         "bs",
41370         "1242"
41371       ],
41372       [
41373         "Bahrain (‫البحرين‬‎)",
41374         "bh",
41375         "973"
41376       ],
41377       [
41378         "Bangladesh (বাংলাদেশ)",
41379         "bd",
41380         "880"
41381       ],
41382       [
41383         "Barbados",
41384         "bb",
41385         "1246"
41386       ],
41387       [
41388         "Belarus (Беларусь)",
41389         "by",
41390         "375"
41391       ],
41392       [
41393         "Belgium (België)",
41394         "be",
41395         "32"
41396       ],
41397       [
41398         "Belize",
41399         "bz",
41400         "501"
41401       ],
41402       [
41403         "Benin (Bénin)",
41404         "bj",
41405         "229"
41406       ],
41407       [
41408         "Bermuda",
41409         "bm",
41410         "1441"
41411       ],
41412       [
41413         "Bhutan (འབྲུག)",
41414         "bt",
41415         "975"
41416       ],
41417       [
41418         "Bolivia",
41419         "bo",
41420         "591"
41421       ],
41422       [
41423         "Bosnia and Herzegovina (Босна и Херцеговина)",
41424         "ba",
41425         "387"
41426       ],
41427       [
41428         "Botswana",
41429         "bw",
41430         "267"
41431       ],
41432       [
41433         "Brazil (Brasil)",
41434         "br",
41435         "55"
41436       ],
41437       [
41438         "British Indian Ocean Territory",
41439         "io",
41440         "246"
41441       ],
41442       [
41443         "British Virgin Islands",
41444         "vg",
41445         "1284"
41446       ],
41447       [
41448         "Brunei",
41449         "bn",
41450         "673"
41451       ],
41452       [
41453         "Bulgaria (България)",
41454         "bg",
41455         "359"
41456       ],
41457       [
41458         "Burkina Faso",
41459         "bf",
41460         "226"
41461       ],
41462       [
41463         "Burundi (Uburundi)",
41464         "bi",
41465         "257"
41466       ],
41467       [
41468         "Cambodia (កម្ពុជា)",
41469         "kh",
41470         "855"
41471       ],
41472       [
41473         "Cameroon (Cameroun)",
41474         "cm",
41475         "237"
41476       ],
41477       [
41478         "Canada",
41479         "ca",
41480         "1",
41481         1,
41482         ["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"]
41483       ],
41484       [
41485         "Cape Verde (Kabu Verdi)",
41486         "cv",
41487         "238"
41488       ],
41489       [
41490         "Caribbean Netherlands",
41491         "bq",
41492         "599",
41493         1
41494       ],
41495       [
41496         "Cayman Islands",
41497         "ky",
41498         "1345"
41499       ],
41500       [
41501         "Central African Republic (République centrafricaine)",
41502         "cf",
41503         "236"
41504       ],
41505       [
41506         "Chad (Tchad)",
41507         "td",
41508         "235"
41509       ],
41510       [
41511         "Chile",
41512         "cl",
41513         "56"
41514       ],
41515       [
41516         "China (中国)",
41517         "cn",
41518         "86"
41519       ],
41520       [
41521         "Christmas Island",
41522         "cx",
41523         "61",
41524         2
41525       ],
41526       [
41527         "Cocos (Keeling) Islands",
41528         "cc",
41529         "61",
41530         1
41531       ],
41532       [
41533         "Colombia",
41534         "co",
41535         "57"
41536       ],
41537       [
41538         "Comoros (‫جزر القمر‬‎)",
41539         "km",
41540         "269"
41541       ],
41542       [
41543         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41544         "cd",
41545         "243"
41546       ],
41547       [
41548         "Congo (Republic) (Congo-Brazzaville)",
41549         "cg",
41550         "242"
41551       ],
41552       [
41553         "Cook Islands",
41554         "ck",
41555         "682"
41556       ],
41557       [
41558         "Costa Rica",
41559         "cr",
41560         "506"
41561       ],
41562       [
41563         "Côte d’Ivoire",
41564         "ci",
41565         "225"
41566       ],
41567       [
41568         "Croatia (Hrvatska)",
41569         "hr",
41570         "385"
41571       ],
41572       [
41573         "Cuba",
41574         "cu",
41575         "53"
41576       ],
41577       [
41578         "Curaçao",
41579         "cw",
41580         "599",
41581         0
41582       ],
41583       [
41584         "Cyprus (Κύπρος)",
41585         "cy",
41586         "357"
41587       ],
41588       [
41589         "Czech Republic (Česká republika)",
41590         "cz",
41591         "420"
41592       ],
41593       [
41594         "Denmark (Danmark)",
41595         "dk",
41596         "45"
41597       ],
41598       [
41599         "Djibouti",
41600         "dj",
41601         "253"
41602       ],
41603       [
41604         "Dominica",
41605         "dm",
41606         "1767"
41607       ],
41608       [
41609         "Dominican Republic (República Dominicana)",
41610         "do",
41611         "1",
41612         2,
41613         ["809", "829", "849"]
41614       ],
41615       [
41616         "Ecuador",
41617         "ec",
41618         "593"
41619       ],
41620       [
41621         "Egypt (‫مصر‬‎)",
41622         "eg",
41623         "20"
41624       ],
41625       [
41626         "El Salvador",
41627         "sv",
41628         "503"
41629       ],
41630       [
41631         "Equatorial Guinea (Guinea Ecuatorial)",
41632         "gq",
41633         "240"
41634       ],
41635       [
41636         "Eritrea",
41637         "er",
41638         "291"
41639       ],
41640       [
41641         "Estonia (Eesti)",
41642         "ee",
41643         "372"
41644       ],
41645       [
41646         "Ethiopia",
41647         "et",
41648         "251"
41649       ],
41650       [
41651         "Falkland Islands (Islas Malvinas)",
41652         "fk",
41653         "500"
41654       ],
41655       [
41656         "Faroe Islands (Føroyar)",
41657         "fo",
41658         "298"
41659       ],
41660       [
41661         "Fiji",
41662         "fj",
41663         "679"
41664       ],
41665       [
41666         "Finland (Suomi)",
41667         "fi",
41668         "358",
41669         0
41670       ],
41671       [
41672         "France",
41673         "fr",
41674         "33"
41675       ],
41676       [
41677         "French Guiana (Guyane française)",
41678         "gf",
41679         "594"
41680       ],
41681       [
41682         "French Polynesia (Polynésie française)",
41683         "pf",
41684         "689"
41685       ],
41686       [
41687         "Gabon",
41688         "ga",
41689         "241"
41690       ],
41691       [
41692         "Gambia",
41693         "gm",
41694         "220"
41695       ],
41696       [
41697         "Georgia (საქართველო)",
41698         "ge",
41699         "995"
41700       ],
41701       [
41702         "Germany (Deutschland)",
41703         "de",
41704         "49"
41705       ],
41706       [
41707         "Ghana (Gaana)",
41708         "gh",
41709         "233"
41710       ],
41711       [
41712         "Gibraltar",
41713         "gi",
41714         "350"
41715       ],
41716       [
41717         "Greece (Ελλάδα)",
41718         "gr",
41719         "30"
41720       ],
41721       [
41722         "Greenland (Kalaallit Nunaat)",
41723         "gl",
41724         "299"
41725       ],
41726       [
41727         "Grenada",
41728         "gd",
41729         "1473"
41730       ],
41731       [
41732         "Guadeloupe",
41733         "gp",
41734         "590",
41735         0
41736       ],
41737       [
41738         "Guam",
41739         "gu",
41740         "1671"
41741       ],
41742       [
41743         "Guatemala",
41744         "gt",
41745         "502"
41746       ],
41747       [
41748         "Guernsey",
41749         "gg",
41750         "44",
41751         1
41752       ],
41753       [
41754         "Guinea (Guinée)",
41755         "gn",
41756         "224"
41757       ],
41758       [
41759         "Guinea-Bissau (Guiné Bissau)",
41760         "gw",
41761         "245"
41762       ],
41763       [
41764         "Guyana",
41765         "gy",
41766         "592"
41767       ],
41768       [
41769         "Haiti",
41770         "ht",
41771         "509"
41772       ],
41773       [
41774         "Honduras",
41775         "hn",
41776         "504"
41777       ],
41778       [
41779         "Hong Kong (香港)",
41780         "hk",
41781         "852"
41782       ],
41783       [
41784         "Hungary (Magyarország)",
41785         "hu",
41786         "36"
41787       ],
41788       [
41789         "Iceland (Ísland)",
41790         "is",
41791         "354"
41792       ],
41793       [
41794         "India (भारत)",
41795         "in",
41796         "91"
41797       ],
41798       [
41799         "Indonesia",
41800         "id",
41801         "62"
41802       ],
41803       [
41804         "Iran (‫ایران‬‎)",
41805         "ir",
41806         "98"
41807       ],
41808       [
41809         "Iraq (‫العراق‬‎)",
41810         "iq",
41811         "964"
41812       ],
41813       [
41814         "Ireland",
41815         "ie",
41816         "353"
41817       ],
41818       [
41819         "Isle of Man",
41820         "im",
41821         "44",
41822         2
41823       ],
41824       [
41825         "Israel (‫ישראל‬‎)",
41826         "il",
41827         "972"
41828       ],
41829       [
41830         "Italy (Italia)",
41831         "it",
41832         "39",
41833         0
41834       ],
41835       [
41836         "Jamaica",
41837         "jm",
41838         "1876"
41839       ],
41840       [
41841         "Japan (日本)",
41842         "jp",
41843         "81"
41844       ],
41845       [
41846         "Jersey",
41847         "je",
41848         "44",
41849         3
41850       ],
41851       [
41852         "Jordan (‫الأردن‬‎)",
41853         "jo",
41854         "962"
41855       ],
41856       [
41857         "Kazakhstan (Казахстан)",
41858         "kz",
41859         "7",
41860         1
41861       ],
41862       [
41863         "Kenya",
41864         "ke",
41865         "254"
41866       ],
41867       [
41868         "Kiribati",
41869         "ki",
41870         "686"
41871       ],
41872       [
41873         "Kosovo",
41874         "xk",
41875         "383"
41876       ],
41877       [
41878         "Kuwait (‫الكويت‬‎)",
41879         "kw",
41880         "965"
41881       ],
41882       [
41883         "Kyrgyzstan (Кыргызстан)",
41884         "kg",
41885         "996"
41886       ],
41887       [
41888         "Laos (ລາວ)",
41889         "la",
41890         "856"
41891       ],
41892       [
41893         "Latvia (Latvija)",
41894         "lv",
41895         "371"
41896       ],
41897       [
41898         "Lebanon (‫لبنان‬‎)",
41899         "lb",
41900         "961"
41901       ],
41902       [
41903         "Lesotho",
41904         "ls",
41905         "266"
41906       ],
41907       [
41908         "Liberia",
41909         "lr",
41910         "231"
41911       ],
41912       [
41913         "Libya (‫ليبيا‬‎)",
41914         "ly",
41915         "218"
41916       ],
41917       [
41918         "Liechtenstein",
41919         "li",
41920         "423"
41921       ],
41922       [
41923         "Lithuania (Lietuva)",
41924         "lt",
41925         "370"
41926       ],
41927       [
41928         "Luxembourg",
41929         "lu",
41930         "352"
41931       ],
41932       [
41933         "Macau (澳門)",
41934         "mo",
41935         "853"
41936       ],
41937       [
41938         "Macedonia (FYROM) (Македонија)",
41939         "mk",
41940         "389"
41941       ],
41942       [
41943         "Madagascar (Madagasikara)",
41944         "mg",
41945         "261"
41946       ],
41947       [
41948         "Malawi",
41949         "mw",
41950         "265"
41951       ],
41952       [
41953         "Malaysia",
41954         "my",
41955         "60"
41956       ],
41957       [
41958         "Maldives",
41959         "mv",
41960         "960"
41961       ],
41962       [
41963         "Mali",
41964         "ml",
41965         "223"
41966       ],
41967       [
41968         "Malta",
41969         "mt",
41970         "356"
41971       ],
41972       [
41973         "Marshall Islands",
41974         "mh",
41975         "692"
41976       ],
41977       [
41978         "Martinique",
41979         "mq",
41980         "596"
41981       ],
41982       [
41983         "Mauritania (‫موريتانيا‬‎)",
41984         "mr",
41985         "222"
41986       ],
41987       [
41988         "Mauritius (Moris)",
41989         "mu",
41990         "230"
41991       ],
41992       [
41993         "Mayotte",
41994         "yt",
41995         "262",
41996         1
41997       ],
41998       [
41999         "Mexico (México)",
42000         "mx",
42001         "52"
42002       ],
42003       [
42004         "Micronesia",
42005         "fm",
42006         "691"
42007       ],
42008       [
42009         "Moldova (Republica Moldova)",
42010         "md",
42011         "373"
42012       ],
42013       [
42014         "Monaco",
42015         "mc",
42016         "377"
42017       ],
42018       [
42019         "Mongolia (Монгол)",
42020         "mn",
42021         "976"
42022       ],
42023       [
42024         "Montenegro (Crna Gora)",
42025         "me",
42026         "382"
42027       ],
42028       [
42029         "Montserrat",
42030         "ms",
42031         "1664"
42032       ],
42033       [
42034         "Morocco (‫المغرب‬‎)",
42035         "ma",
42036         "212",
42037         0
42038       ],
42039       [
42040         "Mozambique (Moçambique)",
42041         "mz",
42042         "258"
42043       ],
42044       [
42045         "Myanmar (Burma) (မြန်မာ)",
42046         "mm",
42047         "95"
42048       ],
42049       [
42050         "Namibia (Namibië)",
42051         "na",
42052         "264"
42053       ],
42054       [
42055         "Nauru",
42056         "nr",
42057         "674"
42058       ],
42059       [
42060         "Nepal (नेपाल)",
42061         "np",
42062         "977"
42063       ],
42064       [
42065         "Netherlands (Nederland)",
42066         "nl",
42067         "31"
42068       ],
42069       [
42070         "New Caledonia (Nouvelle-Calédonie)",
42071         "nc",
42072         "687"
42073       ],
42074       [
42075         "New Zealand",
42076         "nz",
42077         "64"
42078       ],
42079       [
42080         "Nicaragua",
42081         "ni",
42082         "505"
42083       ],
42084       [
42085         "Niger (Nijar)",
42086         "ne",
42087         "227"
42088       ],
42089       [
42090         "Nigeria",
42091         "ng",
42092         "234"
42093       ],
42094       [
42095         "Niue",
42096         "nu",
42097         "683"
42098       ],
42099       [
42100         "Norfolk Island",
42101         "nf",
42102         "672"
42103       ],
42104       [
42105         "North Korea (조선 민주주의 인민 공화국)",
42106         "kp",
42107         "850"
42108       ],
42109       [
42110         "Northern Mariana Islands",
42111         "mp",
42112         "1670"
42113       ],
42114       [
42115         "Norway (Norge)",
42116         "no",
42117         "47",
42118         0
42119       ],
42120       [
42121         "Oman (‫عُمان‬‎)",
42122         "om",
42123         "968"
42124       ],
42125       [
42126         "Pakistan (‫پاکستان‬‎)",
42127         "pk",
42128         "92"
42129       ],
42130       [
42131         "Palau",
42132         "pw",
42133         "680"
42134       ],
42135       [
42136         "Palestine (‫فلسطين‬‎)",
42137         "ps",
42138         "970"
42139       ],
42140       [
42141         "Panama (Panamá)",
42142         "pa",
42143         "507"
42144       ],
42145       [
42146         "Papua New Guinea",
42147         "pg",
42148         "675"
42149       ],
42150       [
42151         "Paraguay",
42152         "py",
42153         "595"
42154       ],
42155       [
42156         "Peru (Perú)",
42157         "pe",
42158         "51"
42159       ],
42160       [
42161         "Philippines",
42162         "ph",
42163         "63"
42164       ],
42165       [
42166         "Poland (Polska)",
42167         "pl",
42168         "48"
42169       ],
42170       [
42171         "Portugal",
42172         "pt",
42173         "351"
42174       ],
42175       [
42176         "Puerto Rico",
42177         "pr",
42178         "1",
42179         3,
42180         ["787", "939"]
42181       ],
42182       [
42183         "Qatar (‫قطر‬‎)",
42184         "qa",
42185         "974"
42186       ],
42187       [
42188         "Réunion (La Réunion)",
42189         "re",
42190         "262",
42191         0
42192       ],
42193       [
42194         "Romania (România)",
42195         "ro",
42196         "40"
42197       ],
42198       [
42199         "Russia (Россия)",
42200         "ru",
42201         "7",
42202         0
42203       ],
42204       [
42205         "Rwanda",
42206         "rw",
42207         "250"
42208       ],
42209       [
42210         "Saint Barthélemy",
42211         "bl",
42212         "590",
42213         1
42214       ],
42215       [
42216         "Saint Helena",
42217         "sh",
42218         "290"
42219       ],
42220       [
42221         "Saint Kitts and Nevis",
42222         "kn",
42223         "1869"
42224       ],
42225       [
42226         "Saint Lucia",
42227         "lc",
42228         "1758"
42229       ],
42230       [
42231         "Saint Martin (Saint-Martin (partie française))",
42232         "mf",
42233         "590",
42234         2
42235       ],
42236       [
42237         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42238         "pm",
42239         "508"
42240       ],
42241       [
42242         "Saint Vincent and the Grenadines",
42243         "vc",
42244         "1784"
42245       ],
42246       [
42247         "Samoa",
42248         "ws",
42249         "685"
42250       ],
42251       [
42252         "San Marino",
42253         "sm",
42254         "378"
42255       ],
42256       [
42257         "São Tomé and Príncipe (São Tomé e Príncipe)",
42258         "st",
42259         "239"
42260       ],
42261       [
42262         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42263         "sa",
42264         "966"
42265       ],
42266       [
42267         "Senegal (Sénégal)",
42268         "sn",
42269         "221"
42270       ],
42271       [
42272         "Serbia (Србија)",
42273         "rs",
42274         "381"
42275       ],
42276       [
42277         "Seychelles",
42278         "sc",
42279         "248"
42280       ],
42281       [
42282         "Sierra Leone",
42283         "sl",
42284         "232"
42285       ],
42286       [
42287         "Singapore",
42288         "sg",
42289         "65"
42290       ],
42291       [
42292         "Sint Maarten",
42293         "sx",
42294         "1721"
42295       ],
42296       [
42297         "Slovakia (Slovensko)",
42298         "sk",
42299         "421"
42300       ],
42301       [
42302         "Slovenia (Slovenija)",
42303         "si",
42304         "386"
42305       ],
42306       [
42307         "Solomon Islands",
42308         "sb",
42309         "677"
42310       ],
42311       [
42312         "Somalia (Soomaaliya)",
42313         "so",
42314         "252"
42315       ],
42316       [
42317         "South Africa",
42318         "za",
42319         "27"
42320       ],
42321       [
42322         "South Korea (대한민국)",
42323         "kr",
42324         "82"
42325       ],
42326       [
42327         "South Sudan (‫جنوب السودان‬‎)",
42328         "ss",
42329         "211"
42330       ],
42331       [
42332         "Spain (España)",
42333         "es",
42334         "34"
42335       ],
42336       [
42337         "Sri Lanka (ශ්‍රී ලංකාව)",
42338         "lk",
42339         "94"
42340       ],
42341       [
42342         "Sudan (‫السودان‬‎)",
42343         "sd",
42344         "249"
42345       ],
42346       [
42347         "Suriname",
42348         "sr",
42349         "597"
42350       ],
42351       [
42352         "Svalbard and Jan Mayen",
42353         "sj",
42354         "47",
42355         1
42356       ],
42357       [
42358         "Swaziland",
42359         "sz",
42360         "268"
42361       ],
42362       [
42363         "Sweden (Sverige)",
42364         "se",
42365         "46"
42366       ],
42367       [
42368         "Switzerland (Schweiz)",
42369         "ch",
42370         "41"
42371       ],
42372       [
42373         "Syria (‫سوريا‬‎)",
42374         "sy",
42375         "963"
42376       ],
42377       [
42378         "Taiwan (台灣)",
42379         "tw",
42380         "886"
42381       ],
42382       [
42383         "Tajikistan",
42384         "tj",
42385         "992"
42386       ],
42387       [
42388         "Tanzania",
42389         "tz",
42390         "255"
42391       ],
42392       [
42393         "Thailand (ไทย)",
42394         "th",
42395         "66"
42396       ],
42397       [
42398         "Timor-Leste",
42399         "tl",
42400         "670"
42401       ],
42402       [
42403         "Togo",
42404         "tg",
42405         "228"
42406       ],
42407       [
42408         "Tokelau",
42409         "tk",
42410         "690"
42411       ],
42412       [
42413         "Tonga",
42414         "to",
42415         "676"
42416       ],
42417       [
42418         "Trinidad and Tobago",
42419         "tt",
42420         "1868"
42421       ],
42422       [
42423         "Tunisia (‫تونس‬‎)",
42424         "tn",
42425         "216"
42426       ],
42427       [
42428         "Turkey (Türkiye)",
42429         "tr",
42430         "90"
42431       ],
42432       [
42433         "Turkmenistan",
42434         "tm",
42435         "993"
42436       ],
42437       [
42438         "Turks and Caicos Islands",
42439         "tc",
42440         "1649"
42441       ],
42442       [
42443         "Tuvalu",
42444         "tv",
42445         "688"
42446       ],
42447       [
42448         "U.S. Virgin Islands",
42449         "vi",
42450         "1340"
42451       ],
42452       [
42453         "Uganda",
42454         "ug",
42455         "256"
42456       ],
42457       [
42458         "Ukraine (Україна)",
42459         "ua",
42460         "380"
42461       ],
42462       [
42463         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42464         "ae",
42465         "971"
42466       ],
42467       [
42468         "United Kingdom",
42469         "gb",
42470         "44",
42471         0
42472       ],
42473       [
42474         "United States",
42475         "us",
42476         "1",
42477         0
42478       ],
42479       [
42480         "Uruguay",
42481         "uy",
42482         "598"
42483       ],
42484       [
42485         "Uzbekistan (Oʻzbekiston)",
42486         "uz",
42487         "998"
42488       ],
42489       [
42490         "Vanuatu",
42491         "vu",
42492         "678"
42493       ],
42494       [
42495         "Vatican City (Città del Vaticano)",
42496         "va",
42497         "39",
42498         1
42499       ],
42500       [
42501         "Venezuela",
42502         "ve",
42503         "58"
42504       ],
42505       [
42506         "Vietnam (Việt Nam)",
42507         "vn",
42508         "84"
42509       ],
42510       [
42511         "Wallis and Futuna (Wallis-et-Futuna)",
42512         "wf",
42513         "681"
42514       ],
42515       [
42516         "Western Sahara (‫الصحراء الغربية‬‎)",
42517         "eh",
42518         "212",
42519         1
42520       ],
42521       [
42522         "Yemen (‫اليمن‬‎)",
42523         "ye",
42524         "967"
42525       ],
42526       [
42527         "Zambia",
42528         "zm",
42529         "260"
42530       ],
42531       [
42532         "Zimbabwe",
42533         "zw",
42534         "263"
42535       ],
42536       [
42537         "Åland Islands",
42538         "ax",
42539         "358",
42540         1
42541       ]
42542   ];
42543   
42544   return d;
42545 }/**
42546 *    This script refer to:
42547 *    Title: International Telephone Input
42548 *    Author: Jack O'Connor
42549 *    Code version:  v12.1.12
42550 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42551 **/
42552
42553 /**
42554  * @class Roo.bootstrap.PhoneInput
42555  * @extends Roo.bootstrap.TriggerField
42556  * An input with International dial-code selection
42557  
42558  * @cfg {String} defaultDialCode default '+852'
42559  * @cfg {Array} preferedCountries default []
42560   
42561  * @constructor
42562  * Create a new PhoneInput.
42563  * @param {Object} config Configuration options
42564  */
42565
42566 Roo.bootstrap.PhoneInput = function(config) {
42567     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42568 };
42569
42570 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42571         
42572         listWidth: undefined,
42573         
42574         selectedClass: 'active',
42575         
42576         invalidClass : "has-warning",
42577         
42578         validClass: 'has-success',
42579         
42580         allowed: '0123456789',
42581         
42582         max_length: 15,
42583         
42584         /**
42585          * @cfg {String} defaultDialCode The default dial code when initializing the input
42586          */
42587         defaultDialCode: '+852',
42588         
42589         /**
42590          * @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
42591          */
42592         preferedCountries: false,
42593         
42594         getAutoCreate : function()
42595         {
42596             var data = Roo.bootstrap.PhoneInputData();
42597             var align = this.labelAlign || this.parentLabelAlign();
42598             var id = Roo.id();
42599             
42600             this.allCountries = [];
42601             this.dialCodeMapping = [];
42602             
42603             for (var i = 0; i < data.length; i++) {
42604               var c = data[i];
42605               this.allCountries[i] = {
42606                 name: c[0],
42607                 iso2: c[1],
42608                 dialCode: c[2],
42609                 priority: c[3] || 0,
42610                 areaCodes: c[4] || null
42611               };
42612               this.dialCodeMapping[c[2]] = {
42613                   name: c[0],
42614                   iso2: c[1],
42615                   priority: c[3] || 0,
42616                   areaCodes: c[4] || null
42617               };
42618             }
42619             
42620             var cfg = {
42621                 cls: 'form-group',
42622                 cn: []
42623             };
42624             
42625             var input =  {
42626                 tag: 'input',
42627                 id : id,
42628                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42629                 maxlength: this.max_length,
42630                 cls : 'form-control tel-input',
42631                 autocomplete: 'new-password'
42632             };
42633             
42634             var hiddenInput = {
42635                 tag: 'input',
42636                 type: 'hidden',
42637                 cls: 'hidden-tel-input'
42638             };
42639             
42640             if (this.name) {
42641                 hiddenInput.name = this.name;
42642             }
42643             
42644             if (this.disabled) {
42645                 input.disabled = true;
42646             }
42647             
42648             var flag_container = {
42649                 tag: 'div',
42650                 cls: 'flag-box',
42651                 cn: [
42652                     {
42653                         tag: 'div',
42654                         cls: 'flag'
42655                     },
42656                     {
42657                         tag: 'div',
42658                         cls: 'caret'
42659                     }
42660                 ]
42661             };
42662             
42663             var box = {
42664                 tag: 'div',
42665                 cls: this.hasFeedback ? 'has-feedback' : '',
42666                 cn: [
42667                     hiddenInput,
42668                     input,
42669                     {
42670                         tag: 'input',
42671                         cls: 'dial-code-holder',
42672                         disabled: true
42673                     }
42674                 ]
42675             };
42676             
42677             var container = {
42678                 cls: 'roo-select2-container input-group',
42679                 cn: [
42680                     flag_container,
42681                     box
42682                 ]
42683             };
42684             
42685             if (this.fieldLabel.length) {
42686                 var indicator = {
42687                     tag: 'i',
42688                     tooltip: 'This field is required'
42689                 };
42690                 
42691                 var label = {
42692                     tag: 'label',
42693                     'for':  id,
42694                     cls: 'control-label',
42695                     cn: []
42696                 };
42697                 
42698                 var label_text = {
42699                     tag: 'span',
42700                     html: this.fieldLabel
42701                 };
42702                 
42703                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42704                 label.cn = [
42705                     indicator,
42706                     label_text
42707                 ];
42708                 
42709                 if(this.indicatorpos == 'right') {
42710                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42711                     label.cn = [
42712                         label_text,
42713                         indicator
42714                     ];
42715                 }
42716                 
42717                 if(align == 'left') {
42718                     container = {
42719                         tag: 'div',
42720                         cn: [
42721                             container
42722                         ]
42723                     };
42724                     
42725                     if(this.labelWidth > 12){
42726                         label.style = "width: " + this.labelWidth + 'px';
42727                     }
42728                     if(this.labelWidth < 13 && this.labelmd == 0){
42729                         this.labelmd = this.labelWidth;
42730                     }
42731                     if(this.labellg > 0){
42732                         label.cls += ' col-lg-' + this.labellg;
42733                         input.cls += ' col-lg-' + (12 - this.labellg);
42734                     }
42735                     if(this.labelmd > 0){
42736                         label.cls += ' col-md-' + this.labelmd;
42737                         container.cls += ' col-md-' + (12 - this.labelmd);
42738                     }
42739                     if(this.labelsm > 0){
42740                         label.cls += ' col-sm-' + this.labelsm;
42741                         container.cls += ' col-sm-' + (12 - this.labelsm);
42742                     }
42743                     if(this.labelxs > 0){
42744                         label.cls += ' col-xs-' + this.labelxs;
42745                         container.cls += ' col-xs-' + (12 - this.labelxs);
42746                     }
42747                 }
42748             }
42749             
42750             cfg.cn = [
42751                 label,
42752                 container
42753             ];
42754             
42755             var settings = this;
42756             
42757             ['xs','sm','md','lg'].map(function(size){
42758                 if (settings[size]) {
42759                     cfg.cls += ' col-' + size + '-' + settings[size];
42760                 }
42761             });
42762             
42763             this.store = new Roo.data.Store({
42764                 proxy : new Roo.data.MemoryProxy({}),
42765                 reader : new Roo.data.JsonReader({
42766                     fields : [
42767                         {
42768                             'name' : 'name',
42769                             'type' : 'string'
42770                         },
42771                         {
42772                             'name' : 'iso2',
42773                             'type' : 'string'
42774                         },
42775                         {
42776                             'name' : 'dialCode',
42777                             'type' : 'string'
42778                         },
42779                         {
42780                             'name' : 'priority',
42781                             'type' : 'string'
42782                         },
42783                         {
42784                             'name' : 'areaCodes',
42785                             'type' : 'string'
42786                         }
42787                     ]
42788                 })
42789             });
42790             
42791             if(!this.preferedCountries) {
42792                 this.preferedCountries = [
42793                     'hk',
42794                     'gb',
42795                     'us'
42796                 ];
42797             }
42798             
42799             var p = this.preferedCountries.reverse();
42800             
42801             if(p) {
42802                 for (var i = 0; i < p.length; i++) {
42803                     for (var j = 0; j < this.allCountries.length; j++) {
42804                         if(this.allCountries[j].iso2 == p[i]) {
42805                             var t = this.allCountries[j];
42806                             this.allCountries.splice(j,1);
42807                             this.allCountries.unshift(t);
42808                         }
42809                     } 
42810                 }
42811             }
42812             
42813             this.store.proxy.data = {
42814                 success: true,
42815                 data: this.allCountries
42816             };
42817             
42818             return cfg;
42819         },
42820         
42821         initEvents : function()
42822         {
42823             this.createList();
42824             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42825             
42826             this.indicator = this.indicatorEl();
42827             this.flag = this.flagEl();
42828             this.dialCodeHolder = this.dialCodeHolderEl();
42829             
42830             this.trigger = this.el.select('div.flag-box',true).first();
42831             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42832             
42833             var _this = this;
42834             
42835             (function(){
42836                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42837                 _this.list.setWidth(lw);
42838             }).defer(100);
42839             
42840             this.list.on('mouseover', this.onViewOver, this);
42841             this.list.on('mousemove', this.onViewMove, this);
42842             this.inputEl().on("keyup", this.onKeyUp, this);
42843             this.inputEl().on("keypress", this.onKeyPress, this);
42844             
42845             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42846
42847             this.view = new Roo.View(this.list, this.tpl, {
42848                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42849             });
42850             
42851             this.view.on('click', this.onViewClick, this);
42852             this.setValue(this.defaultDialCode);
42853         },
42854         
42855         onTriggerClick : function(e)
42856         {
42857             Roo.log('trigger click');
42858             if(this.disabled){
42859                 return;
42860             }
42861             
42862             if(this.isExpanded()){
42863                 this.collapse();
42864                 this.hasFocus = false;
42865             }else {
42866                 this.store.load({});
42867                 this.hasFocus = true;
42868                 this.expand();
42869             }
42870         },
42871         
42872         isExpanded : function()
42873         {
42874             return this.list.isVisible();
42875         },
42876         
42877         collapse : function()
42878         {
42879             if(!this.isExpanded()){
42880                 return;
42881             }
42882             this.list.hide();
42883             Roo.get(document).un('mousedown', this.collapseIf, this);
42884             Roo.get(document).un('mousewheel', this.collapseIf, this);
42885             this.fireEvent('collapse', this);
42886             this.validate();
42887         },
42888         
42889         expand : function()
42890         {
42891             Roo.log('expand');
42892
42893             if(this.isExpanded() || !this.hasFocus){
42894                 return;
42895             }
42896             
42897             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42898             this.list.setWidth(lw);
42899             
42900             this.list.show();
42901             this.restrictHeight();
42902             
42903             Roo.get(document).on('mousedown', this.collapseIf, this);
42904             Roo.get(document).on('mousewheel', this.collapseIf, this);
42905             
42906             this.fireEvent('expand', this);
42907         },
42908         
42909         restrictHeight : function()
42910         {
42911             this.list.alignTo(this.inputEl(), this.listAlign);
42912             this.list.alignTo(this.inputEl(), this.listAlign);
42913         },
42914         
42915         onViewOver : function(e, t)
42916         {
42917             if(this.inKeyMode){
42918                 return;
42919             }
42920             var item = this.view.findItemFromChild(t);
42921             
42922             if(item){
42923                 var index = this.view.indexOf(item);
42924                 this.select(index, false);
42925             }
42926         },
42927
42928         // private
42929         onViewClick : function(view, doFocus, el, e)
42930         {
42931             var index = this.view.getSelectedIndexes()[0];
42932             
42933             var r = this.store.getAt(index);
42934             
42935             if(r){
42936                 this.onSelect(r, index);
42937             }
42938             if(doFocus !== false && !this.blockFocus){
42939                 this.inputEl().focus();
42940             }
42941         },
42942         
42943         onViewMove : function(e, t)
42944         {
42945             this.inKeyMode = false;
42946         },
42947         
42948         select : function(index, scrollIntoView)
42949         {
42950             this.selectedIndex = index;
42951             this.view.select(index);
42952             if(scrollIntoView !== false){
42953                 var el = this.view.getNode(index);
42954                 if(el){
42955                     this.list.scrollChildIntoView(el, false);
42956                 }
42957             }
42958         },
42959         
42960         createList : function()
42961         {
42962             this.list = Roo.get(document.body).createChild({
42963                 tag: 'ul',
42964                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42965                 style: 'display:none'
42966             });
42967             
42968             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42969         },
42970         
42971         collapseIf : function(e)
42972         {
42973             var in_combo  = e.within(this.el);
42974             var in_list =  e.within(this.list);
42975             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42976             
42977             if (in_combo || in_list || is_list) {
42978                 return;
42979             }
42980             this.collapse();
42981         },
42982         
42983         onSelect : function(record, index)
42984         {
42985             if(this.fireEvent('beforeselect', this, record, index) !== false){
42986                 
42987                 this.setFlagClass(record.data.iso2);
42988                 this.setDialCode(record.data.dialCode);
42989                 this.hasFocus = false;
42990                 this.collapse();
42991                 this.fireEvent('select', this, record, index);
42992             }
42993         },
42994         
42995         flagEl : function()
42996         {
42997             var flag = this.el.select('div.flag',true).first();
42998             if(!flag){
42999                 return false;
43000             }
43001             return flag;
43002         },
43003         
43004         dialCodeHolderEl : function()
43005         {
43006             var d = this.el.select('input.dial-code-holder',true).first();
43007             if(!d){
43008                 return false;
43009             }
43010             return d;
43011         },
43012         
43013         setDialCode : function(v)
43014         {
43015             this.dialCodeHolder.dom.value = '+'+v;
43016         },
43017         
43018         setFlagClass : function(n)
43019         {
43020             this.flag.dom.className = 'flag '+n;
43021         },
43022         
43023         getValue : function()
43024         {
43025             var v = this.inputEl().getValue();
43026             if(this.dialCodeHolder) {
43027                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43028             }
43029             return v;
43030         },
43031         
43032         setValue : function(v)
43033         {
43034             var d = this.getDialCode(v);
43035             
43036             //invalid dial code
43037             if(v.length == 0 || !d || d.length == 0) {
43038                 if(this.rendered){
43039                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43040                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43041                 }
43042                 return;
43043             }
43044             
43045             //valid dial code
43046             this.setFlagClass(this.dialCodeMapping[d].iso2);
43047             this.setDialCode(d);
43048             this.inputEl().dom.value = v.replace('+'+d,'');
43049             this.hiddenEl().dom.value = this.getValue();
43050             
43051             this.validate();
43052         },
43053         
43054         getDialCode : function(v)
43055         {
43056             v = v ||  '';
43057             
43058             if (v.length == 0) {
43059                 return this.dialCodeHolder.dom.value;
43060             }
43061             
43062             var dialCode = "";
43063             if (v.charAt(0) != "+") {
43064                 return false;
43065             }
43066             var numericChars = "";
43067             for (var i = 1; i < v.length; i++) {
43068               var c = v.charAt(i);
43069               if (!isNaN(c)) {
43070                 numericChars += c;
43071                 if (this.dialCodeMapping[numericChars]) {
43072                   dialCode = v.substr(1, i);
43073                 }
43074                 if (numericChars.length == 4) {
43075                   break;
43076                 }
43077               }
43078             }
43079             return dialCode;
43080         },
43081         
43082         reset : function()
43083         {
43084             this.setValue(this.defaultDialCode);
43085             this.validate();
43086         },
43087         
43088         hiddenEl : function()
43089         {
43090             return this.el.select('input.hidden-tel-input',true).first();
43091         },
43092         
43093         // after setting val
43094         onKeyUp : function(e){
43095             this.setValue(this.getValue());
43096         },
43097         
43098         onKeyPress : function(e){
43099             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43100                 e.stopEvent();
43101             }
43102         }
43103         
43104 });
43105 /**
43106  * @class Roo.bootstrap.MoneyField
43107  * @extends Roo.bootstrap.ComboBox
43108  * Bootstrap MoneyField class
43109  * 
43110  * @constructor
43111  * Create a new MoneyField.
43112  * @param {Object} config Configuration options
43113  */
43114
43115 Roo.bootstrap.MoneyField = function(config) {
43116     
43117     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43118     
43119 };
43120
43121 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43122     
43123     /**
43124      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43125      */
43126     allowDecimals : true,
43127     /**
43128      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43129      */
43130     decimalSeparator : ".",
43131     /**
43132      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43133      */
43134     decimalPrecision : 0,
43135     /**
43136      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43137      */
43138     allowNegative : true,
43139     /**
43140      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43141      */
43142     allowZero: true,
43143     /**
43144      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43145      */
43146     minValue : Number.NEGATIVE_INFINITY,
43147     /**
43148      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43149      */
43150     maxValue : Number.MAX_VALUE,
43151     /**
43152      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43153      */
43154     minText : "The minimum value for this field is {0}",
43155     /**
43156      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43157      */
43158     maxText : "The maximum value for this field is {0}",
43159     /**
43160      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43161      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43162      */
43163     nanText : "{0} is not a valid number",
43164     /**
43165      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43166      */
43167     castInt : true,
43168     /**
43169      * @cfg {String} defaults currency of the MoneyField
43170      * value should be in lkey
43171      */
43172     defaultCurrency : false,
43173     /**
43174      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43175      */
43176     thousandsDelimiter : false,
43177     /**
43178      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43179      */
43180     max_length: false,
43181     
43182     inputlg : 9,
43183     inputmd : 9,
43184     inputsm : 9,
43185     inputxs : 6,
43186     
43187     store : false,
43188     
43189     getAutoCreate : function()
43190     {
43191         var align = this.labelAlign || this.parentLabelAlign();
43192         
43193         var id = Roo.id();
43194
43195         var cfg = {
43196             cls: 'form-group',
43197             cn: []
43198         };
43199
43200         var input =  {
43201             tag: 'input',
43202             id : id,
43203             cls : 'form-control roo-money-amount-input',
43204             autocomplete: 'new-password'
43205         };
43206         
43207         var hiddenInput = {
43208             tag: 'input',
43209             type: 'hidden',
43210             id: Roo.id(),
43211             cls: 'hidden-number-input'
43212         };
43213         
43214         if(this.max_length) {
43215             input.maxlength = this.max_length; 
43216         }
43217         
43218         if (this.name) {
43219             hiddenInput.name = this.name;
43220         }
43221
43222         if (this.disabled) {
43223             input.disabled = true;
43224         }
43225
43226         var clg = 12 - this.inputlg;
43227         var cmd = 12 - this.inputmd;
43228         var csm = 12 - this.inputsm;
43229         var cxs = 12 - this.inputxs;
43230         
43231         var container = {
43232             tag : 'div',
43233             cls : 'row roo-money-field',
43234             cn : [
43235                 {
43236                     tag : 'div',
43237                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43238                     cn : [
43239                         {
43240                             tag : 'div',
43241                             cls: 'roo-select2-container input-group',
43242                             cn: [
43243                                 {
43244                                     tag : 'input',
43245                                     cls : 'form-control roo-money-currency-input',
43246                                     autocomplete: 'new-password',
43247                                     readOnly : 1,
43248                                     name : this.currencyName
43249                                 },
43250                                 {
43251                                     tag :'span',
43252                                     cls : 'input-group-addon',
43253                                     cn : [
43254                                         {
43255                                             tag: 'span',
43256                                             cls: 'caret'
43257                                         }
43258                                     ]
43259                                 }
43260                             ]
43261                         }
43262                     ]
43263                 },
43264                 {
43265                     tag : 'div',
43266                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43267                     cn : [
43268                         {
43269                             tag: 'div',
43270                             cls: this.hasFeedback ? 'has-feedback' : '',
43271                             cn: [
43272                                 input
43273                             ]
43274                         }
43275                     ]
43276                 }
43277             ]
43278             
43279         };
43280         
43281         if (this.fieldLabel.length) {
43282             var indicator = {
43283                 tag: 'i',
43284                 tooltip: 'This field is required'
43285             };
43286
43287             var label = {
43288                 tag: 'label',
43289                 'for':  id,
43290                 cls: 'control-label',
43291                 cn: []
43292             };
43293
43294             var label_text = {
43295                 tag: 'span',
43296                 html: this.fieldLabel
43297             };
43298
43299             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43300             label.cn = [
43301                 indicator,
43302                 label_text
43303             ];
43304
43305             if(this.indicatorpos == 'right') {
43306                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43307                 label.cn = [
43308                     label_text,
43309                     indicator
43310                 ];
43311             }
43312
43313             if(align == 'left') {
43314                 container = {
43315                     tag: 'div',
43316                     cn: [
43317                         container
43318                     ]
43319                 };
43320
43321                 if(this.labelWidth > 12){
43322                     label.style = "width: " + this.labelWidth + 'px';
43323                 }
43324                 if(this.labelWidth < 13 && this.labelmd == 0){
43325                     this.labelmd = this.labelWidth;
43326                 }
43327                 if(this.labellg > 0){
43328                     label.cls += ' col-lg-' + this.labellg;
43329                     input.cls += ' col-lg-' + (12 - this.labellg);
43330                 }
43331                 if(this.labelmd > 0){
43332                     label.cls += ' col-md-' + this.labelmd;
43333                     container.cls += ' col-md-' + (12 - this.labelmd);
43334                 }
43335                 if(this.labelsm > 0){
43336                     label.cls += ' col-sm-' + this.labelsm;
43337                     container.cls += ' col-sm-' + (12 - this.labelsm);
43338                 }
43339                 if(this.labelxs > 0){
43340                     label.cls += ' col-xs-' + this.labelxs;
43341                     container.cls += ' col-xs-' + (12 - this.labelxs);
43342                 }
43343             }
43344         }
43345
43346         cfg.cn = [
43347             label,
43348             container,
43349             hiddenInput
43350         ];
43351         
43352         var settings = this;
43353
43354         ['xs','sm','md','lg'].map(function(size){
43355             if (settings[size]) {
43356                 cfg.cls += ' col-' + size + '-' + settings[size];
43357             }
43358         });
43359         
43360         return cfg;
43361     },
43362     
43363     initEvents : function()
43364     {
43365         this.indicator = this.indicatorEl();
43366         
43367         this.initCurrencyEvent();
43368         
43369         this.initNumberEvent();
43370     },
43371     
43372     initCurrencyEvent : function()
43373     {
43374         if (!this.store) {
43375             throw "can not find store for combo";
43376         }
43377         
43378         this.store = Roo.factory(this.store, Roo.data);
43379         this.store.parent = this;
43380         
43381         this.createList();
43382         
43383         this.triggerEl = this.el.select('.input-group-addon', true).first();
43384         
43385         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43386         
43387         var _this = this;
43388         
43389         (function(){
43390             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43391             _this.list.setWidth(lw);
43392         }).defer(100);
43393         
43394         this.list.on('mouseover', this.onViewOver, this);
43395         this.list.on('mousemove', this.onViewMove, this);
43396         this.list.on('scroll', this.onViewScroll, this);
43397         
43398         if(!this.tpl){
43399             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43400         }
43401         
43402         this.view = new Roo.View(this.list, this.tpl, {
43403             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43404         });
43405         
43406         this.view.on('click', this.onViewClick, this);
43407         
43408         this.store.on('beforeload', this.onBeforeLoad, this);
43409         this.store.on('load', this.onLoad, this);
43410         this.store.on('loadexception', this.onLoadException, this);
43411         
43412         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43413             "up" : function(e){
43414                 this.inKeyMode = true;
43415                 this.selectPrev();
43416             },
43417
43418             "down" : function(e){
43419                 if(!this.isExpanded()){
43420                     this.onTriggerClick();
43421                 }else{
43422                     this.inKeyMode = true;
43423                     this.selectNext();
43424                 }
43425             },
43426
43427             "enter" : function(e){
43428                 this.collapse();
43429                 
43430                 if(this.fireEvent("specialkey", this, e)){
43431                     this.onViewClick(false);
43432                 }
43433                 
43434                 return true;
43435             },
43436
43437             "esc" : function(e){
43438                 this.collapse();
43439             },
43440
43441             "tab" : function(e){
43442                 this.collapse();
43443                 
43444                 if(this.fireEvent("specialkey", this, e)){
43445                     this.onViewClick(false);
43446                 }
43447                 
43448                 return true;
43449             },
43450
43451             scope : this,
43452
43453             doRelay : function(foo, bar, hname){
43454                 if(hname == 'down' || this.scope.isExpanded()){
43455                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43456                 }
43457                 return true;
43458             },
43459
43460             forceKeyDown: true
43461         });
43462         
43463         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43464         
43465     },
43466     
43467     initNumberEvent : function(e)
43468     {
43469         this.inputEl().on("keydown" , this.fireKey,  this);
43470         this.inputEl().on("focus", this.onFocus,  this);
43471         this.inputEl().on("blur", this.onBlur,  this);
43472         
43473         this.inputEl().relayEvent('keyup', this);
43474         
43475         if(this.indicator){
43476             this.indicator.addClass('invisible');
43477         }
43478  
43479         this.originalValue = this.getValue();
43480         
43481         if(this.validationEvent == 'keyup'){
43482             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43483             this.inputEl().on('keyup', this.filterValidation, this);
43484         }
43485         else if(this.validationEvent !== false){
43486             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43487         }
43488         
43489         if(this.selectOnFocus){
43490             this.on("focus", this.preFocus, this);
43491             
43492         }
43493         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43494             this.inputEl().on("keypress", this.filterKeys, this);
43495         } else {
43496             this.inputEl().relayEvent('keypress', this);
43497         }
43498         
43499         var allowed = "0123456789";
43500         
43501         if(this.allowDecimals){
43502             allowed += this.decimalSeparator;
43503         }
43504         
43505         if(this.allowNegative){
43506             allowed += "-";
43507         }
43508         
43509         if(this.thousandsDelimiter) {
43510             allowed += ",";
43511         }
43512         
43513         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43514         
43515         var keyPress = function(e){
43516             
43517             var k = e.getKey();
43518             
43519             var c = e.getCharCode();
43520             
43521             if(
43522                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43523                     allowed.indexOf(String.fromCharCode(c)) === -1
43524             ){
43525                 e.stopEvent();
43526                 return;
43527             }
43528             
43529             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43530                 return;
43531             }
43532             
43533             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43534                 e.stopEvent();
43535             }
43536         };
43537         
43538         this.inputEl().on("keypress", keyPress, this);
43539         
43540     },
43541     
43542     onTriggerClick : function(e)
43543     {   
43544         if(this.disabled){
43545             return;
43546         }
43547         
43548         this.page = 0;
43549         this.loadNext = false;
43550         
43551         if(this.isExpanded()){
43552             this.collapse();
43553             return;
43554         }
43555         
43556         this.hasFocus = true;
43557         
43558         if(this.triggerAction == 'all') {
43559             this.doQuery(this.allQuery, true);
43560             return;
43561         }
43562         
43563         this.doQuery(this.getRawValue());
43564     },
43565     
43566     getCurrency : function()
43567     {   
43568         var v = this.currencyEl().getValue();
43569         
43570         return v;
43571     },
43572     
43573     restrictHeight : function()
43574     {
43575         this.list.alignTo(this.currencyEl(), this.listAlign);
43576         this.list.alignTo(this.currencyEl(), this.listAlign);
43577     },
43578     
43579     onViewClick : function(view, doFocus, el, e)
43580     {
43581         var index = this.view.getSelectedIndexes()[0];
43582         
43583         var r = this.store.getAt(index);
43584         
43585         if(r){
43586             this.onSelect(r, index);
43587         }
43588     },
43589     
43590     onSelect : function(record, index){
43591         
43592         if(this.fireEvent('beforeselect', this, record, index) !== false){
43593         
43594             this.setFromCurrencyData(index > -1 ? record.data : false);
43595             
43596             this.collapse();
43597             
43598             this.fireEvent('select', this, record, index);
43599         }
43600     },
43601     
43602     setFromCurrencyData : function(o)
43603     {
43604         var currency = '';
43605         
43606         this.lastCurrency = o;
43607         
43608         if (this.currencyField) {
43609             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43610         } else {
43611             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43612         }
43613         
43614         this.lastSelectionText = currency;
43615         
43616         //setting default currency
43617         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43618             this.setCurrency(this.defaultCurrency);
43619             return;
43620         }
43621         
43622         this.setCurrency(currency);
43623     },
43624     
43625     setFromData : function(o)
43626     {
43627         var c = {};
43628         
43629         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43630         
43631         this.setFromCurrencyData(c);
43632         
43633         var value = '';
43634         
43635         if (this.name) {
43636             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43637         } else {
43638             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43639         }
43640         
43641         this.setValue(value);
43642         
43643     },
43644     
43645     setCurrency : function(v)
43646     {   
43647         this.currencyValue = v;
43648         
43649         if(this.rendered){
43650             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43651             this.validate();
43652         }
43653     },
43654     
43655     setValue : function(v)
43656     {
43657         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43658         
43659         this.value = v;
43660         
43661         if(this.rendered){
43662             
43663             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43664             
43665             this.inputEl().dom.value = (v == '') ? '' :
43666                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43667             
43668             if(!this.allowZero && v === '0') {
43669                 this.hiddenEl().dom.value = '';
43670                 this.inputEl().dom.value = '';
43671             }
43672             
43673             this.validate();
43674         }
43675     },
43676     
43677     getRawValue : function()
43678     {
43679         var v = this.inputEl().getValue();
43680         
43681         return v;
43682     },
43683     
43684     getValue : function()
43685     {
43686         return this.fixPrecision(this.parseValue(this.getRawValue()));
43687     },
43688     
43689     parseValue : function(value)
43690     {
43691         if(this.thousandsDelimiter) {
43692             value += "";
43693             r = new RegExp(",", "g");
43694             value = value.replace(r, "");
43695         }
43696         
43697         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43698         return isNaN(value) ? '' : value;
43699         
43700     },
43701     
43702     fixPrecision : function(value)
43703     {
43704         if(this.thousandsDelimiter) {
43705             value += "";
43706             r = new RegExp(",", "g");
43707             value = value.replace(r, "");
43708         }
43709         
43710         var nan = isNaN(value);
43711         
43712         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43713             return nan ? '' : value;
43714         }
43715         return parseFloat(value).toFixed(this.decimalPrecision);
43716     },
43717     
43718     decimalPrecisionFcn : function(v)
43719     {
43720         return Math.floor(v);
43721     },
43722     
43723     validateValue : function(value)
43724     {
43725         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43726             return false;
43727         }
43728         
43729         var num = this.parseValue(value);
43730         
43731         if(isNaN(num)){
43732             this.markInvalid(String.format(this.nanText, value));
43733             return false;
43734         }
43735         
43736         if(num < this.minValue){
43737             this.markInvalid(String.format(this.minText, this.minValue));
43738             return false;
43739         }
43740         
43741         if(num > this.maxValue){
43742             this.markInvalid(String.format(this.maxText, this.maxValue));
43743             return false;
43744         }
43745         
43746         return true;
43747     },
43748     
43749     validate : function()
43750     {
43751         if(this.disabled || this.allowBlank){
43752             this.markValid();
43753             return true;
43754         }
43755         
43756         var currency = this.getCurrency();
43757         
43758         if(this.validateValue(this.getRawValue()) && currency.length){
43759             this.markValid();
43760             return true;
43761         }
43762         
43763         this.markInvalid();
43764         return false;
43765     },
43766     
43767     getName: function()
43768     {
43769         return this.name;
43770     },
43771     
43772     beforeBlur : function()
43773     {
43774         if(!this.castInt){
43775             return;
43776         }
43777         
43778         var v = this.parseValue(this.getRawValue());
43779         
43780         if(v || v == 0){
43781             this.setValue(v);
43782         }
43783     },
43784     
43785     onBlur : function()
43786     {
43787         this.beforeBlur();
43788         
43789         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43790             //this.el.removeClass(this.focusClass);
43791         }
43792         
43793         this.hasFocus = false;
43794         
43795         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43796             this.validate();
43797         }
43798         
43799         var v = this.getValue();
43800         
43801         if(String(v) !== String(this.startValue)){
43802             this.fireEvent('change', this, v, this.startValue);
43803         }
43804         
43805         this.fireEvent("blur", this);
43806     },
43807     
43808     inputEl : function()
43809     {
43810         return this.el.select('.roo-money-amount-input', true).first();
43811     },
43812     
43813     currencyEl : function()
43814     {
43815         return this.el.select('.roo-money-currency-input', true).first();
43816     },
43817     
43818     hiddenEl : function()
43819     {
43820         return this.el.select('input.hidden-number-input',true).first();
43821     }
43822     
43823 });/**
43824  * @class Roo.bootstrap.BezierSignature
43825  * @extends Roo.bootstrap.Component
43826  * Bootstrap BezierSignature class
43827  * This script refer to:
43828  *    Title: Signature Pad
43829  *    Author: szimek
43830  *    Availability: https://github.com/szimek/signature_pad
43831  *
43832  * @constructor
43833  * Create a new BezierSignature
43834  * @param {Object} config The config object
43835  */
43836
43837 Roo.bootstrap.BezierSignature = function(config){
43838     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43839     this.addEvents({
43840         "resize" : true
43841     });
43842 };
43843
43844 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43845 {
43846      
43847     curve_data: [],
43848     
43849     is_empty: true,
43850     
43851     mouse_btn_down: true,
43852     
43853     /**
43854      * @cfg {int} canvas height
43855      */
43856     canvas_height: '200px',
43857     
43858     /**
43859      * @cfg {float|function} Radius of a single dot.
43860      */ 
43861     dot_size: false,
43862     
43863     /**
43864      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43865      */
43866     min_width: 0.5,
43867     
43868     /**
43869      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43870      */
43871     max_width: 2.5,
43872     
43873     /**
43874      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43875      */
43876     throttle: 16,
43877     
43878     /**
43879      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43880      */
43881     min_distance: 5,
43882     
43883     /**
43884      * @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.
43885      */
43886     bg_color: 'rgba(0, 0, 0, 0)',
43887     
43888     /**
43889      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43890      */
43891     dot_color: 'black',
43892     
43893     /**
43894      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43895      */ 
43896     velocity_filter_weight: 0.7,
43897     
43898     /**
43899      * @cfg {function} Callback when stroke begin. 
43900      */
43901     onBegin: false,
43902     
43903     /**
43904      * @cfg {function} Callback when stroke end.
43905      */
43906     onEnd: false,
43907     
43908     getAutoCreate : function()
43909     {
43910         var cls = 'roo-signature column';
43911         
43912         if(this.cls){
43913             cls += ' ' + this.cls;
43914         }
43915         
43916         var col_sizes = [
43917             'lg',
43918             'md',
43919             'sm',
43920             'xs'
43921         ];
43922         
43923         for(var i = 0; i < col_sizes.length; i++) {
43924             if(this[col_sizes[i]]) {
43925                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43926             }
43927         }
43928         
43929         var cfg = {
43930             tag: 'div',
43931             cls: cls,
43932             cn: [
43933                 {
43934                     tag: 'div',
43935                     cls: 'roo-signature-body',
43936                     cn: [
43937                         {
43938                             tag: 'canvas',
43939                             cls: 'roo-signature-body-canvas',
43940                             height: this.canvas_height,
43941                             width: this.canvas_width
43942                         }
43943                     ]
43944                 },
43945                 {
43946                     tag: 'input',
43947                     type: 'file',
43948                     style: 'display: none'
43949                 }
43950             ]
43951         };
43952         
43953         return cfg;
43954     },
43955     
43956     initEvents: function() 
43957     {
43958         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43959         
43960         var canvas = this.canvasEl();
43961         
43962         // mouse && touch event swapping...
43963         canvas.dom.style.touchAction = 'none';
43964         canvas.dom.style.msTouchAction = 'none';
43965         
43966         this.mouse_btn_down = false;
43967         canvas.on('mousedown', this._handleMouseDown, this);
43968         canvas.on('mousemove', this._handleMouseMove, this);
43969         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43970         
43971         if (window.PointerEvent) {
43972             canvas.on('pointerdown', this._handleMouseDown, this);
43973             canvas.on('pointermove', this._handleMouseMove, this);
43974             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43975         }
43976         
43977         if ('ontouchstart' in window) {
43978             canvas.on('touchstart', this._handleTouchStart, this);
43979             canvas.on('touchmove', this._handleTouchMove, this);
43980             canvas.on('touchend', this._handleTouchEnd, this);
43981         }
43982         
43983         Roo.EventManager.onWindowResize(this.resize, this, true);
43984         
43985         // file input event
43986         this.fileEl().on('change', this.uploadImage, this);
43987         
43988         this.clear();
43989         
43990         this.resize();
43991     },
43992     
43993     resize: function(){
43994         
43995         var canvas = this.canvasEl().dom;
43996         var ctx = this.canvasElCtx();
43997         var img_data = false;
43998         
43999         if(canvas.width > 0) {
44000             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44001         }
44002         // setting canvas width will clean img data
44003         canvas.width = 0;
44004         
44005         var style = window.getComputedStyle ? 
44006             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44007             
44008         var padding_left = parseInt(style.paddingLeft) || 0;
44009         var padding_right = parseInt(style.paddingRight) || 0;
44010         
44011         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44012         
44013         if(img_data) {
44014             ctx.putImageData(img_data, 0, 0);
44015         }
44016     },
44017     
44018     _handleMouseDown: function(e)
44019     {
44020         if (e.browserEvent.which === 1) {
44021             this.mouse_btn_down = true;
44022             this.strokeBegin(e);
44023         }
44024     },
44025     
44026     _handleMouseMove: function (e)
44027     {
44028         if (this.mouse_btn_down) {
44029             this.strokeMoveUpdate(e);
44030         }
44031     },
44032     
44033     _handleMouseUp: function (e)
44034     {
44035         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44036             this.mouse_btn_down = false;
44037             this.strokeEnd(e);
44038         }
44039     },
44040     
44041     _handleTouchStart: function (e) {
44042         
44043         e.preventDefault();
44044         if (e.browserEvent.targetTouches.length === 1) {
44045             // var touch = e.browserEvent.changedTouches[0];
44046             // this.strokeBegin(touch);
44047             
44048              this.strokeBegin(e); // assume e catching the correct xy...
44049         }
44050     },
44051     
44052     _handleTouchMove: function (e) {
44053         e.preventDefault();
44054         // var touch = event.targetTouches[0];
44055         // _this._strokeMoveUpdate(touch);
44056         this.strokeMoveUpdate(e);
44057     },
44058     
44059     _handleTouchEnd: function (e) {
44060         var wasCanvasTouched = e.target === this.canvasEl().dom;
44061         if (wasCanvasTouched) {
44062             e.preventDefault();
44063             // var touch = event.changedTouches[0];
44064             // _this._strokeEnd(touch);
44065             this.strokeEnd(e);
44066         }
44067     },
44068     
44069     reset: function () {
44070         this._lastPoints = [];
44071         this._lastVelocity = 0;
44072         this._lastWidth = (this.min_width + this.max_width) / 2;
44073         this.canvasElCtx().fillStyle = this.dot_color;
44074     },
44075     
44076     strokeMoveUpdate: function(e)
44077     {
44078         this.strokeUpdate(e);
44079         
44080         if (this.throttle) {
44081             this.throttleStroke(this.strokeUpdate, this.throttle);
44082         }
44083         else {
44084             this.strokeUpdate(e);
44085         }
44086     },
44087     
44088     strokeBegin: function(e)
44089     {
44090         var newPointGroup = {
44091             color: this.dot_color,
44092             points: []
44093         };
44094         
44095         if (typeof this.onBegin === 'function') {
44096             this.onBegin(e);
44097         }
44098         
44099         this.curve_data.push(newPointGroup);
44100         this.reset();
44101         this.strokeUpdate(e);
44102     },
44103     
44104     strokeUpdate: function(e)
44105     {
44106         var rect = this.canvasEl().dom.getBoundingClientRect();
44107         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44108         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44109         var lastPoints = lastPointGroup.points;
44110         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44111         var isLastPointTooClose = lastPoint
44112             ? point.distanceTo(lastPoint) <= this.min_distance
44113             : false;
44114         var color = lastPointGroup.color;
44115         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44116             var curve = this.addPoint(point);
44117             if (!lastPoint) {
44118                 this.drawDot({color: color, point: point});
44119             }
44120             else if (curve) {
44121                 this.drawCurve({color: color, curve: curve});
44122             }
44123             lastPoints.push({
44124                 time: point.time,
44125                 x: point.x,
44126                 y: point.y
44127             });
44128         }
44129     },
44130     
44131     strokeEnd: function(e)
44132     {
44133         this.strokeUpdate(e);
44134         if (typeof this.onEnd === 'function') {
44135             this.onEnd(e);
44136         }
44137     },
44138     
44139     addPoint:  function (point) {
44140         var _lastPoints = this._lastPoints;
44141         _lastPoints.push(point);
44142         if (_lastPoints.length > 2) {
44143             if (_lastPoints.length === 3) {
44144                 _lastPoints.unshift(_lastPoints[0]);
44145             }
44146             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44147             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44148             _lastPoints.shift();
44149             return curve;
44150         }
44151         return null;
44152     },
44153     
44154     calculateCurveWidths: function (startPoint, endPoint) {
44155         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44156             (1 - this.velocity_filter_weight) * this._lastVelocity;
44157
44158         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44159         var widths = {
44160             end: newWidth,
44161             start: this._lastWidth
44162         };
44163         
44164         this._lastVelocity = velocity;
44165         this._lastWidth = newWidth;
44166         return widths;
44167     },
44168     
44169     drawDot: function (_a) {
44170         var color = _a.color, point = _a.point;
44171         var ctx = this.canvasElCtx();
44172         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44173         ctx.beginPath();
44174         this.drawCurveSegment(point.x, point.y, width);
44175         ctx.closePath();
44176         ctx.fillStyle = color;
44177         ctx.fill();
44178     },
44179     
44180     drawCurve: function (_a) {
44181         var color = _a.color, curve = _a.curve;
44182         var ctx = this.canvasElCtx();
44183         var widthDelta = curve.endWidth - curve.startWidth;
44184         var drawSteps = Math.floor(curve.length()) * 2;
44185         ctx.beginPath();
44186         ctx.fillStyle = color;
44187         for (var i = 0; i < drawSteps; i += 1) {
44188         var t = i / drawSteps;
44189         var tt = t * t;
44190         var ttt = tt * t;
44191         var u = 1 - t;
44192         var uu = u * u;
44193         var uuu = uu * u;
44194         var x = uuu * curve.startPoint.x;
44195         x += 3 * uu * t * curve.control1.x;
44196         x += 3 * u * tt * curve.control2.x;
44197         x += ttt * curve.endPoint.x;
44198         var y = uuu * curve.startPoint.y;
44199         y += 3 * uu * t * curve.control1.y;
44200         y += 3 * u * tt * curve.control2.y;
44201         y += ttt * curve.endPoint.y;
44202         var width = curve.startWidth + ttt * widthDelta;
44203         this.drawCurveSegment(x, y, width);
44204         }
44205         ctx.closePath();
44206         ctx.fill();
44207     },
44208     
44209     drawCurveSegment: function (x, y, width) {
44210         var ctx = this.canvasElCtx();
44211         ctx.moveTo(x, y);
44212         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44213         this.is_empty = false;
44214     },
44215     
44216     clear: function()
44217     {
44218         var ctx = this.canvasElCtx();
44219         var canvas = this.canvasEl().dom;
44220         ctx.fillStyle = this.bg_color;
44221         ctx.clearRect(0, 0, canvas.width, canvas.height);
44222         ctx.fillRect(0, 0, canvas.width, canvas.height);
44223         this.curve_data = [];
44224         this.reset();
44225         this.is_empty = true;
44226     },
44227     
44228     fileEl: function()
44229     {
44230         return  this.el.select('input',true).first();
44231     },
44232     
44233     canvasEl: function()
44234     {
44235         return this.el.select('canvas',true).first();
44236     },
44237     
44238     canvasElCtx: function()
44239     {
44240         return this.el.select('canvas',true).first().dom.getContext('2d');
44241     },
44242     
44243     getImage: function(type)
44244     {
44245         if(this.is_empty) {
44246             return false;
44247         }
44248         
44249         // encryption ?
44250         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44251     },
44252     
44253     drawFromImage: function(img_src)
44254     {
44255         var img = new Image();
44256         
44257         img.onload = function(){
44258             this.canvasElCtx().drawImage(img, 0, 0);
44259         }.bind(this);
44260         
44261         img.src = img_src;
44262         
44263         this.is_empty = false;
44264     },
44265     
44266     selectImage: function()
44267     {
44268         this.fileEl().dom.click();
44269     },
44270     
44271     uploadImage: function(e)
44272     {
44273         var reader = new FileReader();
44274         
44275         reader.onload = function(e){
44276             var img = new Image();
44277             img.onload = function(){
44278                 this.reset();
44279                 this.canvasElCtx().drawImage(img, 0, 0);
44280             }.bind(this);
44281             img.src = e.target.result;
44282         }.bind(this);
44283         
44284         reader.readAsDataURL(e.target.files[0]);
44285     },
44286     
44287     // Bezier Point Constructor
44288     Point: (function () {
44289         function Point(x, y, time) {
44290             this.x = x;
44291             this.y = y;
44292             this.time = time || Date.now();
44293         }
44294         Point.prototype.distanceTo = function (start) {
44295             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44296         };
44297         Point.prototype.equals = function (other) {
44298             return this.x === other.x && this.y === other.y && this.time === other.time;
44299         };
44300         Point.prototype.velocityFrom = function (start) {
44301             return this.time !== start.time
44302             ? this.distanceTo(start) / (this.time - start.time)
44303             : 0;
44304         };
44305         return Point;
44306     }()),
44307     
44308     
44309     // Bezier Constructor
44310     Bezier: (function () {
44311         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44312             this.startPoint = startPoint;
44313             this.control2 = control2;
44314             this.control1 = control1;
44315             this.endPoint = endPoint;
44316             this.startWidth = startWidth;
44317             this.endWidth = endWidth;
44318         }
44319         Bezier.fromPoints = function (points, widths, scope) {
44320             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44321             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44322             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44323         };
44324         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44325             var dx1 = s1.x - s2.x;
44326             var dy1 = s1.y - s2.y;
44327             var dx2 = s2.x - s3.x;
44328             var dy2 = s2.y - s3.y;
44329             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44330             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44331             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44332             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44333             var dxm = m1.x - m2.x;
44334             var dym = m1.y - m2.y;
44335             var k = l2 / (l1 + l2);
44336             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44337             var tx = s2.x - cm.x;
44338             var ty = s2.y - cm.y;
44339             return {
44340                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44341                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44342             };
44343         };
44344         Bezier.prototype.length = function () {
44345             var steps = 10;
44346             var length = 0;
44347             var px;
44348             var py;
44349             for (var i = 0; i <= steps; i += 1) {
44350                 var t = i / steps;
44351                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44352                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44353                 if (i > 0) {
44354                     var xdiff = cx - px;
44355                     var ydiff = cy - py;
44356                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44357                 }
44358                 px = cx;
44359                 py = cy;
44360             }
44361             return length;
44362         };
44363         Bezier.prototype.point = function (t, start, c1, c2, end) {
44364             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44365             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44366             + (3.0 * c2 * (1.0 - t) * t * t)
44367             + (end * t * t * t);
44368         };
44369         return Bezier;
44370     }()),
44371     
44372     throttleStroke: function(fn, wait) {
44373       if (wait === void 0) { wait = 250; }
44374       var previous = 0;
44375       var timeout = null;
44376       var result;
44377       var storedContext;
44378       var storedArgs;
44379       var later = function () {
44380           previous = Date.now();
44381           timeout = null;
44382           result = fn.apply(storedContext, storedArgs);
44383           if (!timeout) {
44384               storedContext = null;
44385               storedArgs = [];
44386           }
44387       };
44388       return function wrapper() {
44389           var args = [];
44390           for (var _i = 0; _i < arguments.length; _i++) {
44391               args[_i] = arguments[_i];
44392           }
44393           var now = Date.now();
44394           var remaining = wait - (now - previous);
44395           storedContext = this;
44396           storedArgs = args;
44397           if (remaining <= 0 || remaining > wait) {
44398               if (timeout) {
44399                   clearTimeout(timeout);
44400                   timeout = null;
44401               }
44402               previous = now;
44403               result = fn.apply(storedContext, storedArgs);
44404               if (!timeout) {
44405                   storedContext = null;
44406                   storedArgs = [];
44407               }
44408           }
44409           else if (!timeout) {
44410               timeout = window.setTimeout(later, remaining);
44411           }
44412           return result;
44413       };
44414   }
44415   
44416 });
44417
44418  
44419
44420