roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['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      * @param {string} (left|right|top|bottom) where to try and put it
19937      * @param {Boolean} try and move it if we cant get right position.
19938      */
19939     updatePosition : function(placement, try_move)
19940     {
19941         this.el.addClass(placement + ' roo-popover-' + placement);
19942         
19943         if (!this.alignEl ) {
19944             return false;
19945         }
19946         
19947         switch (placement) {
19948             case 'right':
19949                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19950                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19951                 if (exact.equals(offset)) {
19952                     //normal display...
19953                     this.setXY(exact, this.preanim(arguments, 3));
19954                     var xy = this.alignEl.getAnchorXY(p1, false);
19955                     
19956                 }
19957                 
19958                 
19959         }
19960         
19961         
19962         
19963         // work out the pointy position.
19964         var p1 = this.alignment[0].split('-').pop().replace('?','');
19965         var xy = this.alignEl.getAnchorXY(p1, false);
19966         xy[0]+=2;xy[1]+=5;
19967         this.arrowEl.setXY(xy);
19968         
19969     },
19970     
19971     hide : function()
19972     {
19973         this.el.setXY([0,0]);
19974         this.el.removeClass('in');
19975         this.el.hide();
19976         this.hoverState = null;
19977         this.maskEl.hide(); // always..
19978         this.fireEvent('hide', this);
19979     }
19980     
19981 });
19982
19983
19984 Roo.apply(Roo.bootstrap.Popover, {
19985
19986     alignment : {
19987         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19988         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19989         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19990         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19991     },
19992     
19993     zIndex : 20001,
19994
19995     clickHander : false,
19996     
19997
19998     onMouseDown : function(e)
19999     {
20000         if (!e.getTarget(".roo-popover")) {
20001             this.hideAll();
20002         }
20003          
20004     },
20005     
20006     popups : [],
20007     
20008     register : function(popup)
20009     {
20010         if (!Roo.bootstrap.Popover.clickHandler) {
20011             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20012         }
20013         // hide other popups.
20014         this.hideAll();
20015         this.popups.push(popup);
20016     },
20017     hideAll : function()
20018     {
20019         this.popups.forEach(function(p) {
20020             p.hide();
20021         });
20022     }
20023
20024 });/*
20025  * - LGPL
20026  *
20027  * Card header - holder for the card header elements.
20028  * 
20029  */
20030
20031 /**
20032  * @class Roo.bootstrap.PopoverNav
20033  * @extends Roo.bootstrap.NavGroup
20034  * Bootstrap Popover header navigation class
20035  * @constructor
20036  * Create a new Popover Header Navigation 
20037  * @param {Object} config The config object
20038  */
20039
20040 Roo.bootstrap.PopoverNav = function(config){
20041     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20042 };
20043
20044 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20045     
20046     
20047     container_method : 'getPopoverHeader' 
20048     
20049      
20050     
20051     
20052    
20053 });
20054
20055  
20056
20057  /*
20058  * - LGPL
20059  *
20060  * Progress
20061  * 
20062  */
20063
20064 /**
20065  * @class Roo.bootstrap.Progress
20066  * @extends Roo.bootstrap.Component
20067  * Bootstrap Progress class
20068  * @cfg {Boolean} striped striped of the progress bar
20069  * @cfg {Boolean} active animated of the progress bar
20070  * 
20071  * 
20072  * @constructor
20073  * Create a new Progress
20074  * @param {Object} config The config object
20075  */
20076
20077 Roo.bootstrap.Progress = function(config){
20078     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20079 };
20080
20081 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20082     
20083     striped : false,
20084     active: false,
20085     
20086     getAutoCreate : function(){
20087         var cfg = {
20088             tag: 'div',
20089             cls: 'progress'
20090         };
20091         
20092         
20093         if(this.striped){
20094             cfg.cls += ' progress-striped';
20095         }
20096       
20097         if(this.active){
20098             cfg.cls += ' active';
20099         }
20100         
20101         
20102         return cfg;
20103     }
20104    
20105 });
20106
20107  
20108
20109  /*
20110  * - LGPL
20111  *
20112  * ProgressBar
20113  * 
20114  */
20115
20116 /**
20117  * @class Roo.bootstrap.ProgressBar
20118  * @extends Roo.bootstrap.Component
20119  * Bootstrap ProgressBar class
20120  * @cfg {Number} aria_valuenow aria-value now
20121  * @cfg {Number} aria_valuemin aria-value min
20122  * @cfg {Number} aria_valuemax aria-value max
20123  * @cfg {String} label label for the progress bar
20124  * @cfg {String} panel (success | info | warning | danger )
20125  * @cfg {String} role role of the progress bar
20126  * @cfg {String} sr_only text
20127  * 
20128  * 
20129  * @constructor
20130  * Create a new ProgressBar
20131  * @param {Object} config The config object
20132  */
20133
20134 Roo.bootstrap.ProgressBar = function(config){
20135     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20136 };
20137
20138 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20139     
20140     aria_valuenow : 0,
20141     aria_valuemin : 0,
20142     aria_valuemax : 100,
20143     label : false,
20144     panel : false,
20145     role : false,
20146     sr_only: false,
20147     
20148     getAutoCreate : function()
20149     {
20150         
20151         var cfg = {
20152             tag: 'div',
20153             cls: 'progress-bar',
20154             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20155         };
20156         
20157         if(this.sr_only){
20158             cfg.cn = {
20159                 tag: 'span',
20160                 cls: 'sr-only',
20161                 html: this.sr_only
20162             }
20163         }
20164         
20165         if(this.role){
20166             cfg.role = this.role;
20167         }
20168         
20169         if(this.aria_valuenow){
20170             cfg['aria-valuenow'] = this.aria_valuenow;
20171         }
20172         
20173         if(this.aria_valuemin){
20174             cfg['aria-valuemin'] = this.aria_valuemin;
20175         }
20176         
20177         if(this.aria_valuemax){
20178             cfg['aria-valuemax'] = this.aria_valuemax;
20179         }
20180         
20181         if(this.label && !this.sr_only){
20182             cfg.html = this.label;
20183         }
20184         
20185         if(this.panel){
20186             cfg.cls += ' progress-bar-' + this.panel;
20187         }
20188         
20189         return cfg;
20190     },
20191     
20192     update : function(aria_valuenow)
20193     {
20194         this.aria_valuenow = aria_valuenow;
20195         
20196         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20197     }
20198    
20199 });
20200
20201  
20202
20203  /*
20204  * - LGPL
20205  *
20206  * column
20207  * 
20208  */
20209
20210 /**
20211  * @class Roo.bootstrap.TabGroup
20212  * @extends Roo.bootstrap.Column
20213  * Bootstrap Column class
20214  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20215  * @cfg {Boolean} carousel true to make the group behave like a carousel
20216  * @cfg {Boolean} bullets show bullets for the panels
20217  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20218  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20219  * @cfg {Boolean} showarrow (true|false) show arrow default true
20220  * 
20221  * @constructor
20222  * Create a new TabGroup
20223  * @param {Object} config The config object
20224  */
20225
20226 Roo.bootstrap.TabGroup = function(config){
20227     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20228     if (!this.navId) {
20229         this.navId = Roo.id();
20230     }
20231     this.tabs = [];
20232     Roo.bootstrap.TabGroup.register(this);
20233     
20234 };
20235
20236 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20237     
20238     carousel : false,
20239     transition : false,
20240     bullets : 0,
20241     timer : 0,
20242     autoslide : false,
20243     slideFn : false,
20244     slideOnTouch : false,
20245     showarrow : true,
20246     
20247     getAutoCreate : function()
20248     {
20249         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20250         
20251         cfg.cls += ' tab-content';
20252         
20253         if (this.carousel) {
20254             cfg.cls += ' carousel slide';
20255             
20256             cfg.cn = [{
20257                cls : 'carousel-inner',
20258                cn : []
20259             }];
20260         
20261             if(this.bullets  && !Roo.isTouch){
20262                 
20263                 var bullets = {
20264                     cls : 'carousel-bullets',
20265                     cn : []
20266                 };
20267                
20268                 if(this.bullets_cls){
20269                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20270                 }
20271                 
20272                 bullets.cn.push({
20273                     cls : 'clear'
20274                 });
20275                 
20276                 cfg.cn[0].cn.push(bullets);
20277             }
20278             
20279             if(this.showarrow){
20280                 cfg.cn[0].cn.push({
20281                     tag : 'div',
20282                     class : 'carousel-arrow',
20283                     cn : [
20284                         {
20285                             tag : 'div',
20286                             class : 'carousel-prev',
20287                             cn : [
20288                                 {
20289                                     tag : 'i',
20290                                     class : 'fa fa-chevron-left'
20291                                 }
20292                             ]
20293                         },
20294                         {
20295                             tag : 'div',
20296                             class : 'carousel-next',
20297                             cn : [
20298                                 {
20299                                     tag : 'i',
20300                                     class : 'fa fa-chevron-right'
20301                                 }
20302                             ]
20303                         }
20304                     ]
20305                 });
20306             }
20307             
20308         }
20309         
20310         return cfg;
20311     },
20312     
20313     initEvents:  function()
20314     {
20315 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20316 //            this.el.on("touchstart", this.onTouchStart, this);
20317 //        }
20318         
20319         if(this.autoslide){
20320             var _this = this;
20321             
20322             this.slideFn = window.setInterval(function() {
20323                 _this.showPanelNext();
20324             }, this.timer);
20325         }
20326         
20327         if(this.showarrow){
20328             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20329             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20330         }
20331         
20332         
20333     },
20334     
20335 //    onTouchStart : function(e, el, o)
20336 //    {
20337 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20338 //            return;
20339 //        }
20340 //        
20341 //        this.showPanelNext();
20342 //    },
20343     
20344     
20345     getChildContainer : function()
20346     {
20347         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20348     },
20349     
20350     /**
20351     * register a Navigation item
20352     * @param {Roo.bootstrap.NavItem} the navitem to add
20353     */
20354     register : function(item)
20355     {
20356         this.tabs.push( item);
20357         item.navId = this.navId; // not really needed..
20358         this.addBullet();
20359     
20360     },
20361     
20362     getActivePanel : function()
20363     {
20364         var r = false;
20365         Roo.each(this.tabs, function(t) {
20366             if (t.active) {
20367                 r = t;
20368                 return false;
20369             }
20370             return null;
20371         });
20372         return r;
20373         
20374     },
20375     getPanelByName : function(n)
20376     {
20377         var r = false;
20378         Roo.each(this.tabs, function(t) {
20379             if (t.tabId == n) {
20380                 r = t;
20381                 return false;
20382             }
20383             return null;
20384         });
20385         return r;
20386     },
20387     indexOfPanel : function(p)
20388     {
20389         var r = false;
20390         Roo.each(this.tabs, function(t,i) {
20391             if (t.tabId == p.tabId) {
20392                 r = i;
20393                 return false;
20394             }
20395             return null;
20396         });
20397         return r;
20398     },
20399     /**
20400      * show a specific panel
20401      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20402      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20403      */
20404     showPanel : function (pan)
20405     {
20406         if(this.transition || typeof(pan) == 'undefined'){
20407             Roo.log("waiting for the transitionend");
20408             return false;
20409         }
20410         
20411         if (typeof(pan) == 'number') {
20412             pan = this.tabs[pan];
20413         }
20414         
20415         if (typeof(pan) == 'string') {
20416             pan = this.getPanelByName(pan);
20417         }
20418         
20419         var cur = this.getActivePanel();
20420         
20421         if(!pan || !cur){
20422             Roo.log('pan or acitve pan is undefined');
20423             return false;
20424         }
20425         
20426         if (pan.tabId == this.getActivePanel().tabId) {
20427             return true;
20428         }
20429         
20430         if (false === cur.fireEvent('beforedeactivate')) {
20431             return false;
20432         }
20433         
20434         if(this.bullets > 0 && !Roo.isTouch){
20435             this.setActiveBullet(this.indexOfPanel(pan));
20436         }
20437         
20438         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20439             
20440             //class="carousel-item carousel-item-next carousel-item-left"
20441             
20442             this.transition = true;
20443             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20444             var lr = dir == 'next' ? 'left' : 'right';
20445             pan.el.addClass(dir); // or prev
20446             pan.el.addClass('carousel-item-' + dir); // or prev
20447             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20448             cur.el.addClass(lr); // or right
20449             pan.el.addClass(lr);
20450             cur.el.addClass('carousel-item-' +lr); // or right
20451             pan.el.addClass('carousel-item-' +lr);
20452             
20453             
20454             var _this = this;
20455             cur.el.on('transitionend', function() {
20456                 Roo.log("trans end?");
20457                 
20458                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20459                 pan.setActive(true);
20460                 
20461                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20462                 cur.setActive(false);
20463                 
20464                 _this.transition = false;
20465                 
20466             }, this, { single:  true } );
20467             
20468             return true;
20469         }
20470         
20471         cur.setActive(false);
20472         pan.setActive(true);
20473         
20474         return true;
20475         
20476     },
20477     showPanelNext : function()
20478     {
20479         var i = this.indexOfPanel(this.getActivePanel());
20480         
20481         if (i >= this.tabs.length - 1 && !this.autoslide) {
20482             return;
20483         }
20484         
20485         if (i >= this.tabs.length - 1 && this.autoslide) {
20486             i = -1;
20487         }
20488         
20489         this.showPanel(this.tabs[i+1]);
20490     },
20491     
20492     showPanelPrev : function()
20493     {
20494         var i = this.indexOfPanel(this.getActivePanel());
20495         
20496         if (i  < 1 && !this.autoslide) {
20497             return;
20498         }
20499         
20500         if (i < 1 && this.autoslide) {
20501             i = this.tabs.length;
20502         }
20503         
20504         this.showPanel(this.tabs[i-1]);
20505     },
20506     
20507     
20508     addBullet: function()
20509     {
20510         if(!this.bullets || Roo.isTouch){
20511             return;
20512         }
20513         var ctr = this.el.select('.carousel-bullets',true).first();
20514         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20515         var bullet = ctr.createChild({
20516             cls : 'bullet bullet-' + i
20517         },ctr.dom.lastChild);
20518         
20519         
20520         var _this = this;
20521         
20522         bullet.on('click', (function(e, el, o, ii, t){
20523
20524             e.preventDefault();
20525
20526             this.showPanel(ii);
20527
20528             if(this.autoslide && this.slideFn){
20529                 clearInterval(this.slideFn);
20530                 this.slideFn = window.setInterval(function() {
20531                     _this.showPanelNext();
20532                 }, this.timer);
20533             }
20534
20535         }).createDelegate(this, [i, bullet], true));
20536                 
20537         
20538     },
20539      
20540     setActiveBullet : function(i)
20541     {
20542         if(Roo.isTouch){
20543             return;
20544         }
20545         
20546         Roo.each(this.el.select('.bullet', true).elements, function(el){
20547             el.removeClass('selected');
20548         });
20549
20550         var bullet = this.el.select('.bullet-' + i, true).first();
20551         
20552         if(!bullet){
20553             return;
20554         }
20555         
20556         bullet.addClass('selected');
20557     }
20558     
20559     
20560   
20561 });
20562
20563  
20564
20565  
20566  
20567 Roo.apply(Roo.bootstrap.TabGroup, {
20568     
20569     groups: {},
20570      /**
20571     * register a Navigation Group
20572     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20573     */
20574     register : function(navgrp)
20575     {
20576         this.groups[navgrp.navId] = navgrp;
20577         
20578     },
20579     /**
20580     * fetch a Navigation Group based on the navigation ID
20581     * if one does not exist , it will get created.
20582     * @param {string} the navgroup to add
20583     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20584     */
20585     get: function(navId) {
20586         if (typeof(this.groups[navId]) == 'undefined') {
20587             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20588         }
20589         return this.groups[navId] ;
20590     }
20591     
20592     
20593     
20594 });
20595
20596  /*
20597  * - LGPL
20598  *
20599  * TabPanel
20600  * 
20601  */
20602
20603 /**
20604  * @class Roo.bootstrap.TabPanel
20605  * @extends Roo.bootstrap.Component
20606  * Bootstrap TabPanel class
20607  * @cfg {Boolean} active panel active
20608  * @cfg {String} html panel content
20609  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20610  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20611  * @cfg {String} href click to link..
20612  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20613  * 
20614  * 
20615  * @constructor
20616  * Create a new TabPanel
20617  * @param {Object} config The config object
20618  */
20619
20620 Roo.bootstrap.TabPanel = function(config){
20621     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20622     this.addEvents({
20623         /**
20624              * @event changed
20625              * Fires when the active status changes
20626              * @param {Roo.bootstrap.TabPanel} this
20627              * @param {Boolean} state the new state
20628             
20629          */
20630         'changed': true,
20631         /**
20632              * @event beforedeactivate
20633              * Fires before a tab is de-activated - can be used to do validation on a form.
20634              * @param {Roo.bootstrap.TabPanel} this
20635              * @return {Boolean} false if there is an error
20636             
20637          */
20638         'beforedeactivate': true
20639      });
20640     
20641     this.tabId = this.tabId || Roo.id();
20642   
20643 };
20644
20645 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20646     
20647     active: false,
20648     html: false,
20649     tabId: false,
20650     navId : false,
20651     href : '',
20652     touchSlide : false,
20653     getAutoCreate : function(){
20654         
20655         
20656         var cfg = {
20657             tag: 'div',
20658             // item is needed for carousel - not sure if it has any effect otherwise
20659             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20660             html: this.html || ''
20661         };
20662         
20663         if(this.active){
20664             cfg.cls += ' active';
20665         }
20666         
20667         if(this.tabId){
20668             cfg.tabId = this.tabId;
20669         }
20670         
20671         
20672         
20673         return cfg;
20674     },
20675     
20676     initEvents:  function()
20677     {
20678         var p = this.parent();
20679         
20680         this.navId = this.navId || p.navId;
20681         
20682         if (typeof(this.navId) != 'undefined') {
20683             // not really needed.. but just in case.. parent should be a NavGroup.
20684             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20685             
20686             tg.register(this);
20687             
20688             var i = tg.tabs.length - 1;
20689             
20690             if(this.active && tg.bullets > 0 && i < tg.bullets){
20691                 tg.setActiveBullet(i);
20692             }
20693         }
20694         
20695         this.el.on('click', this.onClick, this);
20696         
20697         if(Roo.isTouch && this.touchSlide){
20698             this.el.on("touchstart", this.onTouchStart, this);
20699             this.el.on("touchmove", this.onTouchMove, this);
20700             this.el.on("touchend", this.onTouchEnd, this);
20701         }
20702         
20703     },
20704     
20705     onRender : function(ct, position)
20706     {
20707         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20708     },
20709     
20710     setActive : function(state)
20711     {
20712         Roo.log("panel - set active " + this.tabId + "=" + state);
20713         
20714         this.active = state;
20715         if (!state) {
20716             this.el.removeClass('active');
20717             
20718         } else  if (!this.el.hasClass('active')) {
20719             this.el.addClass('active');
20720         }
20721         
20722         this.fireEvent('changed', this, state);
20723     },
20724     
20725     onClick : function(e)
20726     {
20727         e.preventDefault();
20728         
20729         if(!this.href.length){
20730             return;
20731         }
20732         
20733         window.location.href = this.href;
20734     },
20735     
20736     startX : 0,
20737     startY : 0,
20738     endX : 0,
20739     endY : 0,
20740     swiping : false,
20741     
20742     onTouchStart : function(e)
20743     {
20744         this.swiping = false;
20745         
20746         this.startX = e.browserEvent.touches[0].clientX;
20747         this.startY = e.browserEvent.touches[0].clientY;
20748     },
20749     
20750     onTouchMove : function(e)
20751     {
20752         this.swiping = true;
20753         
20754         this.endX = e.browserEvent.touches[0].clientX;
20755         this.endY = e.browserEvent.touches[0].clientY;
20756     },
20757     
20758     onTouchEnd : function(e)
20759     {
20760         if(!this.swiping){
20761             this.onClick(e);
20762             return;
20763         }
20764         
20765         var tabGroup = this.parent();
20766         
20767         if(this.endX > this.startX){ // swiping right
20768             tabGroup.showPanelPrev();
20769             return;
20770         }
20771         
20772         if(this.startX > this.endX){ // swiping left
20773             tabGroup.showPanelNext();
20774             return;
20775         }
20776     }
20777     
20778     
20779 });
20780  
20781
20782  
20783
20784  /*
20785  * - LGPL
20786  *
20787  * DateField
20788  * 
20789  */
20790
20791 /**
20792  * @class Roo.bootstrap.DateField
20793  * @extends Roo.bootstrap.Input
20794  * Bootstrap DateField class
20795  * @cfg {Number} weekStart default 0
20796  * @cfg {String} viewMode default empty, (months|years)
20797  * @cfg {String} minViewMode default empty, (months|years)
20798  * @cfg {Number} startDate default -Infinity
20799  * @cfg {Number} endDate default Infinity
20800  * @cfg {Boolean} todayHighlight default false
20801  * @cfg {Boolean} todayBtn default false
20802  * @cfg {Boolean} calendarWeeks default false
20803  * @cfg {Object} daysOfWeekDisabled default empty
20804  * @cfg {Boolean} singleMode default false (true | false)
20805  * 
20806  * @cfg {Boolean} keyboardNavigation default true
20807  * @cfg {String} language default en
20808  * 
20809  * @constructor
20810  * Create a new DateField
20811  * @param {Object} config The config object
20812  */
20813
20814 Roo.bootstrap.DateField = function(config){
20815     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20816      this.addEvents({
20817             /**
20818              * @event show
20819              * Fires when this field show.
20820              * @param {Roo.bootstrap.DateField} this
20821              * @param {Mixed} date The date value
20822              */
20823             show : true,
20824             /**
20825              * @event show
20826              * Fires when this field hide.
20827              * @param {Roo.bootstrap.DateField} this
20828              * @param {Mixed} date The date value
20829              */
20830             hide : true,
20831             /**
20832              * @event select
20833              * Fires when select a date.
20834              * @param {Roo.bootstrap.DateField} this
20835              * @param {Mixed} date The date value
20836              */
20837             select : true,
20838             /**
20839              * @event beforeselect
20840              * Fires when before select a date.
20841              * @param {Roo.bootstrap.DateField} this
20842              * @param {Mixed} date The date value
20843              */
20844             beforeselect : true
20845         });
20846 };
20847
20848 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20849     
20850     /**
20851      * @cfg {String} format
20852      * The default date format string which can be overriden for localization support.  The format must be
20853      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20854      */
20855     format : "m/d/y",
20856     /**
20857      * @cfg {String} altFormats
20858      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20859      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20860      */
20861     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20862     
20863     weekStart : 0,
20864     
20865     viewMode : '',
20866     
20867     minViewMode : '',
20868     
20869     todayHighlight : false,
20870     
20871     todayBtn: false,
20872     
20873     language: 'en',
20874     
20875     keyboardNavigation: true,
20876     
20877     calendarWeeks: false,
20878     
20879     startDate: -Infinity,
20880     
20881     endDate: Infinity,
20882     
20883     daysOfWeekDisabled: [],
20884     
20885     _events: [],
20886     
20887     singleMode : false,
20888     
20889     UTCDate: function()
20890     {
20891         return new Date(Date.UTC.apply(Date, arguments));
20892     },
20893     
20894     UTCToday: function()
20895     {
20896         var today = new Date();
20897         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20898     },
20899     
20900     getDate: function() {
20901             var d = this.getUTCDate();
20902             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20903     },
20904     
20905     getUTCDate: function() {
20906             return this.date;
20907     },
20908     
20909     setDate: function(d) {
20910             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20911     },
20912     
20913     setUTCDate: function(d) {
20914             this.date = d;
20915             this.setValue(this.formatDate(this.date));
20916     },
20917         
20918     onRender: function(ct, position)
20919     {
20920         
20921         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20922         
20923         this.language = this.language || 'en';
20924         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20925         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20926         
20927         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20928         this.format = this.format || 'm/d/y';
20929         this.isInline = false;
20930         this.isInput = true;
20931         this.component = this.el.select('.add-on', true).first() || false;
20932         this.component = (this.component && this.component.length === 0) ? false : this.component;
20933         this.hasInput = this.component && this.inputEl().length;
20934         
20935         if (typeof(this.minViewMode === 'string')) {
20936             switch (this.minViewMode) {
20937                 case 'months':
20938                     this.minViewMode = 1;
20939                     break;
20940                 case 'years':
20941                     this.minViewMode = 2;
20942                     break;
20943                 default:
20944                     this.minViewMode = 0;
20945                     break;
20946             }
20947         }
20948         
20949         if (typeof(this.viewMode === 'string')) {
20950             switch (this.viewMode) {
20951                 case 'months':
20952                     this.viewMode = 1;
20953                     break;
20954                 case 'years':
20955                     this.viewMode = 2;
20956                     break;
20957                 default:
20958                     this.viewMode = 0;
20959                     break;
20960             }
20961         }
20962                 
20963         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20964         
20965 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20966         
20967         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20968         
20969         this.picker().on('mousedown', this.onMousedown, this);
20970         this.picker().on('click', this.onClick, this);
20971         
20972         this.picker().addClass('datepicker-dropdown');
20973         
20974         this.startViewMode = this.viewMode;
20975         
20976         if(this.singleMode){
20977             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20978                 v.setVisibilityMode(Roo.Element.DISPLAY);
20979                 v.hide();
20980             });
20981             
20982             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20983                 v.setStyle('width', '189px');
20984             });
20985         }
20986         
20987         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20988             if(!this.calendarWeeks){
20989                 v.remove();
20990                 return;
20991             }
20992             
20993             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20994             v.attr('colspan', function(i, val){
20995                 return parseInt(val) + 1;
20996             });
20997         });
20998                         
20999         
21000         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21001         
21002         this.setStartDate(this.startDate);
21003         this.setEndDate(this.endDate);
21004         
21005         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21006         
21007         this.fillDow();
21008         this.fillMonths();
21009         this.update();
21010         this.showMode();
21011         
21012         if(this.isInline) {
21013             this.showPopup();
21014         }
21015     },
21016     
21017     picker : function()
21018     {
21019         return this.pickerEl;
21020 //        return this.el.select('.datepicker', true).first();
21021     },
21022     
21023     fillDow: function()
21024     {
21025         var dowCnt = this.weekStart;
21026         
21027         var dow = {
21028             tag: 'tr',
21029             cn: [
21030                 
21031             ]
21032         };
21033         
21034         if(this.calendarWeeks){
21035             dow.cn.push({
21036                 tag: 'th',
21037                 cls: 'cw',
21038                 html: '&nbsp;'
21039             })
21040         }
21041         
21042         while (dowCnt < this.weekStart + 7) {
21043             dow.cn.push({
21044                 tag: 'th',
21045                 cls: 'dow',
21046                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21047             });
21048         }
21049         
21050         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21051     },
21052     
21053     fillMonths: function()
21054     {    
21055         var i = 0;
21056         var months = this.picker().select('>.datepicker-months td', true).first();
21057         
21058         months.dom.innerHTML = '';
21059         
21060         while (i < 12) {
21061             var month = {
21062                 tag: 'span',
21063                 cls: 'month',
21064                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21065             };
21066             
21067             months.createChild(month);
21068         }
21069         
21070     },
21071     
21072     update: function()
21073     {
21074         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;
21075         
21076         if (this.date < this.startDate) {
21077             this.viewDate = new Date(this.startDate);
21078         } else if (this.date > this.endDate) {
21079             this.viewDate = new Date(this.endDate);
21080         } else {
21081             this.viewDate = new Date(this.date);
21082         }
21083         
21084         this.fill();
21085     },
21086     
21087     fill: function() 
21088     {
21089         var d = new Date(this.viewDate),
21090                 year = d.getUTCFullYear(),
21091                 month = d.getUTCMonth(),
21092                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21093                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21094                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21095                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21096                 currentDate = this.date && this.date.valueOf(),
21097                 today = this.UTCToday();
21098         
21099         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21100         
21101 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21102         
21103 //        this.picker.select('>tfoot th.today').
21104 //                                              .text(dates[this.language].today)
21105 //                                              .toggle(this.todayBtn !== false);
21106     
21107         this.updateNavArrows();
21108         this.fillMonths();
21109                                                 
21110         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21111         
21112         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21113          
21114         prevMonth.setUTCDate(day);
21115         
21116         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21117         
21118         var nextMonth = new Date(prevMonth);
21119         
21120         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21121         
21122         nextMonth = nextMonth.valueOf();
21123         
21124         var fillMonths = false;
21125         
21126         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21127         
21128         while(prevMonth.valueOf() <= nextMonth) {
21129             var clsName = '';
21130             
21131             if (prevMonth.getUTCDay() === this.weekStart) {
21132                 if(fillMonths){
21133                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21134                 }
21135                     
21136                 fillMonths = {
21137                     tag: 'tr',
21138                     cn: []
21139                 };
21140                 
21141                 if(this.calendarWeeks){
21142                     // ISO 8601: First week contains first thursday.
21143                     // ISO also states week starts on Monday, but we can be more abstract here.
21144                     var
21145                     // Start of current week: based on weekstart/current date
21146                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21147                     // Thursday of this week
21148                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21149                     // First Thursday of year, year from thursday
21150                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21151                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21152                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21153                     
21154                     fillMonths.cn.push({
21155                         tag: 'td',
21156                         cls: 'cw',
21157                         html: calWeek
21158                     });
21159                 }
21160             }
21161             
21162             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21163                 clsName += ' old';
21164             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21165                 clsName += ' new';
21166             }
21167             if (this.todayHighlight &&
21168                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21169                 prevMonth.getUTCMonth() == today.getMonth() &&
21170                 prevMonth.getUTCDate() == today.getDate()) {
21171                 clsName += ' today';
21172             }
21173             
21174             if (currentDate && prevMonth.valueOf() === currentDate) {
21175                 clsName += ' active';
21176             }
21177             
21178             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21179                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21180                     clsName += ' disabled';
21181             }
21182             
21183             fillMonths.cn.push({
21184                 tag: 'td',
21185                 cls: 'day ' + clsName,
21186                 html: prevMonth.getDate()
21187             });
21188             
21189             prevMonth.setDate(prevMonth.getDate()+1);
21190         }
21191           
21192         var currentYear = this.date && this.date.getUTCFullYear();
21193         var currentMonth = this.date && this.date.getUTCMonth();
21194         
21195         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21196         
21197         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21198             v.removeClass('active');
21199             
21200             if(currentYear === year && k === currentMonth){
21201                 v.addClass('active');
21202             }
21203             
21204             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21205                 v.addClass('disabled');
21206             }
21207             
21208         });
21209         
21210         
21211         year = parseInt(year/10, 10) * 10;
21212         
21213         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21214         
21215         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21216         
21217         year -= 1;
21218         for (var i = -1; i < 11; i++) {
21219             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21220                 tag: 'span',
21221                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21222                 html: year
21223             });
21224             
21225             year += 1;
21226         }
21227     },
21228     
21229     showMode: function(dir) 
21230     {
21231         if (dir) {
21232             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21233         }
21234         
21235         Roo.each(this.picker().select('>div',true).elements, function(v){
21236             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21237             v.hide();
21238         });
21239         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21240     },
21241     
21242     place: function()
21243     {
21244         if(this.isInline) {
21245             return;
21246         }
21247         
21248         this.picker().removeClass(['bottom', 'top']);
21249         
21250         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21251             /*
21252              * place to the top of element!
21253              *
21254              */
21255             
21256             this.picker().addClass('top');
21257             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21258             
21259             return;
21260         }
21261         
21262         this.picker().addClass('bottom');
21263         
21264         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21265     },
21266     
21267     parseDate : function(value)
21268     {
21269         if(!value || value instanceof Date){
21270             return value;
21271         }
21272         var v = Date.parseDate(value, this.format);
21273         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21274             v = Date.parseDate(value, 'Y-m-d');
21275         }
21276         if(!v && this.altFormats){
21277             if(!this.altFormatsArray){
21278                 this.altFormatsArray = this.altFormats.split("|");
21279             }
21280             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21281                 v = Date.parseDate(value, this.altFormatsArray[i]);
21282             }
21283         }
21284         return v;
21285     },
21286     
21287     formatDate : function(date, fmt)
21288     {   
21289         return (!date || !(date instanceof Date)) ?
21290         date : date.dateFormat(fmt || this.format);
21291     },
21292     
21293     onFocus : function()
21294     {
21295         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21296         this.showPopup();
21297     },
21298     
21299     onBlur : function()
21300     {
21301         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21302         
21303         var d = this.inputEl().getValue();
21304         
21305         this.setValue(d);
21306                 
21307         this.hidePopup();
21308     },
21309     
21310     showPopup : function()
21311     {
21312         this.picker().show();
21313         this.update();
21314         this.place();
21315         
21316         this.fireEvent('showpopup', this, this.date);
21317     },
21318     
21319     hidePopup : function()
21320     {
21321         if(this.isInline) {
21322             return;
21323         }
21324         this.picker().hide();
21325         this.viewMode = this.startViewMode;
21326         this.showMode();
21327         
21328         this.fireEvent('hidepopup', this, this.date);
21329         
21330     },
21331     
21332     onMousedown: function(e)
21333     {
21334         e.stopPropagation();
21335         e.preventDefault();
21336     },
21337     
21338     keyup: function(e)
21339     {
21340         Roo.bootstrap.DateField.superclass.keyup.call(this);
21341         this.update();
21342     },
21343
21344     setValue: function(v)
21345     {
21346         if(this.fireEvent('beforeselect', this, v) !== false){
21347             var d = new Date(this.parseDate(v) ).clearTime();
21348         
21349             if(isNaN(d.getTime())){
21350                 this.date = this.viewDate = '';
21351                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21352                 return;
21353             }
21354
21355             v = this.formatDate(d);
21356
21357             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21358
21359             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21360
21361             this.update();
21362
21363             this.fireEvent('select', this, this.date);
21364         }
21365     },
21366     
21367     getValue: function()
21368     {
21369         return this.formatDate(this.date);
21370     },
21371     
21372     fireKey: function(e)
21373     {
21374         if (!this.picker().isVisible()){
21375             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21376                 this.showPopup();
21377             }
21378             return;
21379         }
21380         
21381         var dateChanged = false,
21382         dir, day, month,
21383         newDate, newViewDate;
21384         
21385         switch(e.keyCode){
21386             case 27: // escape
21387                 this.hidePopup();
21388                 e.preventDefault();
21389                 break;
21390             case 37: // left
21391             case 39: // right
21392                 if (!this.keyboardNavigation) {
21393                     break;
21394                 }
21395                 dir = e.keyCode == 37 ? -1 : 1;
21396                 
21397                 if (e.ctrlKey){
21398                     newDate = this.moveYear(this.date, dir);
21399                     newViewDate = this.moveYear(this.viewDate, dir);
21400                 } else if (e.shiftKey){
21401                     newDate = this.moveMonth(this.date, dir);
21402                     newViewDate = this.moveMonth(this.viewDate, dir);
21403                 } else {
21404                     newDate = new Date(this.date);
21405                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21406                     newViewDate = new Date(this.viewDate);
21407                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21408                 }
21409                 if (this.dateWithinRange(newDate)){
21410                     this.date = newDate;
21411                     this.viewDate = newViewDate;
21412                     this.setValue(this.formatDate(this.date));
21413 //                    this.update();
21414                     e.preventDefault();
21415                     dateChanged = true;
21416                 }
21417                 break;
21418             case 38: // up
21419             case 40: // down
21420                 if (!this.keyboardNavigation) {
21421                     break;
21422                 }
21423                 dir = e.keyCode == 38 ? -1 : 1;
21424                 if (e.ctrlKey){
21425                     newDate = this.moveYear(this.date, dir);
21426                     newViewDate = this.moveYear(this.viewDate, dir);
21427                 } else if (e.shiftKey){
21428                     newDate = this.moveMonth(this.date, dir);
21429                     newViewDate = this.moveMonth(this.viewDate, dir);
21430                 } else {
21431                     newDate = new Date(this.date);
21432                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21433                     newViewDate = new Date(this.viewDate);
21434                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21435                 }
21436                 if (this.dateWithinRange(newDate)){
21437                     this.date = newDate;
21438                     this.viewDate = newViewDate;
21439                     this.setValue(this.formatDate(this.date));
21440 //                    this.update();
21441                     e.preventDefault();
21442                     dateChanged = true;
21443                 }
21444                 break;
21445             case 13: // enter
21446                 this.setValue(this.formatDate(this.date));
21447                 this.hidePopup();
21448                 e.preventDefault();
21449                 break;
21450             case 9: // tab
21451                 this.setValue(this.formatDate(this.date));
21452                 this.hidePopup();
21453                 break;
21454             case 16: // shift
21455             case 17: // ctrl
21456             case 18: // alt
21457                 break;
21458             default :
21459                 this.hidePopup();
21460                 
21461         }
21462     },
21463     
21464     
21465     onClick: function(e) 
21466     {
21467         e.stopPropagation();
21468         e.preventDefault();
21469         
21470         var target = e.getTarget();
21471         
21472         if(target.nodeName.toLowerCase() === 'i'){
21473             target = Roo.get(target).dom.parentNode;
21474         }
21475         
21476         var nodeName = target.nodeName;
21477         var className = target.className;
21478         var html = target.innerHTML;
21479         //Roo.log(nodeName);
21480         
21481         switch(nodeName.toLowerCase()) {
21482             case 'th':
21483                 switch(className) {
21484                     case 'switch':
21485                         this.showMode(1);
21486                         break;
21487                     case 'prev':
21488                     case 'next':
21489                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21490                         switch(this.viewMode){
21491                                 case 0:
21492                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21493                                         break;
21494                                 case 1:
21495                                 case 2:
21496                                         this.viewDate = this.moveYear(this.viewDate, dir);
21497                                         break;
21498                         }
21499                         this.fill();
21500                         break;
21501                     case 'today':
21502                         var date = new Date();
21503                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21504 //                        this.fill()
21505                         this.setValue(this.formatDate(this.date));
21506                         
21507                         this.hidePopup();
21508                         break;
21509                 }
21510                 break;
21511             case 'span':
21512                 if (className.indexOf('disabled') < 0) {
21513                     this.viewDate.setUTCDate(1);
21514                     if (className.indexOf('month') > -1) {
21515                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21516                     } else {
21517                         var year = parseInt(html, 10) || 0;
21518                         this.viewDate.setUTCFullYear(year);
21519                         
21520                     }
21521                     
21522                     if(this.singleMode){
21523                         this.setValue(this.formatDate(this.viewDate));
21524                         this.hidePopup();
21525                         return;
21526                     }
21527                     
21528                     this.showMode(-1);
21529                     this.fill();
21530                 }
21531                 break;
21532                 
21533             case 'td':
21534                 //Roo.log(className);
21535                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21536                     var day = parseInt(html, 10) || 1;
21537                     var year = this.viewDate.getUTCFullYear(),
21538                         month = this.viewDate.getUTCMonth();
21539
21540                     if (className.indexOf('old') > -1) {
21541                         if(month === 0 ){
21542                             month = 11;
21543                             year -= 1;
21544                         }else{
21545                             month -= 1;
21546                         }
21547                     } else if (className.indexOf('new') > -1) {
21548                         if (month == 11) {
21549                             month = 0;
21550                             year += 1;
21551                         } else {
21552                             month += 1;
21553                         }
21554                     }
21555                     //Roo.log([year,month,day]);
21556                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21557                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21558 //                    this.fill();
21559                     //Roo.log(this.formatDate(this.date));
21560                     this.setValue(this.formatDate(this.date));
21561                     this.hidePopup();
21562                 }
21563                 break;
21564         }
21565     },
21566     
21567     setStartDate: function(startDate)
21568     {
21569         this.startDate = startDate || -Infinity;
21570         if (this.startDate !== -Infinity) {
21571             this.startDate = this.parseDate(this.startDate);
21572         }
21573         this.update();
21574         this.updateNavArrows();
21575     },
21576
21577     setEndDate: function(endDate)
21578     {
21579         this.endDate = endDate || Infinity;
21580         if (this.endDate !== Infinity) {
21581             this.endDate = this.parseDate(this.endDate);
21582         }
21583         this.update();
21584         this.updateNavArrows();
21585     },
21586     
21587     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21588     {
21589         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21590         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21591             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21592         }
21593         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21594             return parseInt(d, 10);
21595         });
21596         this.update();
21597         this.updateNavArrows();
21598     },
21599     
21600     updateNavArrows: function() 
21601     {
21602         if(this.singleMode){
21603             return;
21604         }
21605         
21606         var d = new Date(this.viewDate),
21607         year = d.getUTCFullYear(),
21608         month = d.getUTCMonth();
21609         
21610         Roo.each(this.picker().select('.prev', true).elements, function(v){
21611             v.show();
21612             switch (this.viewMode) {
21613                 case 0:
21614
21615                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21616                         v.hide();
21617                     }
21618                     break;
21619                 case 1:
21620                 case 2:
21621                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21622                         v.hide();
21623                     }
21624                     break;
21625             }
21626         });
21627         
21628         Roo.each(this.picker().select('.next', true).elements, function(v){
21629             v.show();
21630             switch (this.viewMode) {
21631                 case 0:
21632
21633                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21634                         v.hide();
21635                     }
21636                     break;
21637                 case 1:
21638                 case 2:
21639                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21640                         v.hide();
21641                     }
21642                     break;
21643             }
21644         })
21645     },
21646     
21647     moveMonth: function(date, dir)
21648     {
21649         if (!dir) {
21650             return date;
21651         }
21652         var new_date = new Date(date.valueOf()),
21653         day = new_date.getUTCDate(),
21654         month = new_date.getUTCMonth(),
21655         mag = Math.abs(dir),
21656         new_month, test;
21657         dir = dir > 0 ? 1 : -1;
21658         if (mag == 1){
21659             test = dir == -1
21660             // If going back one month, make sure month is not current month
21661             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21662             ? function(){
21663                 return new_date.getUTCMonth() == month;
21664             }
21665             // If going forward one month, make sure month is as expected
21666             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21667             : function(){
21668                 return new_date.getUTCMonth() != new_month;
21669             };
21670             new_month = month + dir;
21671             new_date.setUTCMonth(new_month);
21672             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21673             if (new_month < 0 || new_month > 11) {
21674                 new_month = (new_month + 12) % 12;
21675             }
21676         } else {
21677             // For magnitudes >1, move one month at a time...
21678             for (var i=0; i<mag; i++) {
21679                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21680                 new_date = this.moveMonth(new_date, dir);
21681             }
21682             // ...then reset the day, keeping it in the new month
21683             new_month = new_date.getUTCMonth();
21684             new_date.setUTCDate(day);
21685             test = function(){
21686                 return new_month != new_date.getUTCMonth();
21687             };
21688         }
21689         // Common date-resetting loop -- if date is beyond end of month, make it
21690         // end of month
21691         while (test()){
21692             new_date.setUTCDate(--day);
21693             new_date.setUTCMonth(new_month);
21694         }
21695         return new_date;
21696     },
21697
21698     moveYear: function(date, dir)
21699     {
21700         return this.moveMonth(date, dir*12);
21701     },
21702
21703     dateWithinRange: function(date)
21704     {
21705         return date >= this.startDate && date <= this.endDate;
21706     },
21707
21708     
21709     remove: function() 
21710     {
21711         this.picker().remove();
21712     },
21713     
21714     validateValue : function(value)
21715     {
21716         if(this.getVisibilityEl().hasClass('hidden')){
21717             return true;
21718         }
21719         
21720         if(value.length < 1)  {
21721             if(this.allowBlank){
21722                 return true;
21723             }
21724             return false;
21725         }
21726         
21727         if(value.length < this.minLength){
21728             return false;
21729         }
21730         if(value.length > this.maxLength){
21731             return false;
21732         }
21733         if(this.vtype){
21734             var vt = Roo.form.VTypes;
21735             if(!vt[this.vtype](value, this)){
21736                 return false;
21737             }
21738         }
21739         if(typeof this.validator == "function"){
21740             var msg = this.validator(value);
21741             if(msg !== true){
21742                 return false;
21743             }
21744         }
21745         
21746         if(this.regex && !this.regex.test(value)){
21747             return false;
21748         }
21749         
21750         if(typeof(this.parseDate(value)) == 'undefined'){
21751             return false;
21752         }
21753         
21754         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21755             return false;
21756         }      
21757         
21758         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21759             return false;
21760         } 
21761         
21762         
21763         return true;
21764     },
21765     
21766     reset : function()
21767     {
21768         this.date = this.viewDate = '';
21769         
21770         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21771     }
21772    
21773 });
21774
21775 Roo.apply(Roo.bootstrap.DateField,  {
21776     
21777     head : {
21778         tag: 'thead',
21779         cn: [
21780         {
21781             tag: 'tr',
21782             cn: [
21783             {
21784                 tag: 'th',
21785                 cls: 'prev',
21786                 html: '<i class="fa fa-arrow-left"/>'
21787             },
21788             {
21789                 tag: 'th',
21790                 cls: 'switch',
21791                 colspan: '5'
21792             },
21793             {
21794                 tag: 'th',
21795                 cls: 'next',
21796                 html: '<i class="fa fa-arrow-right"/>'
21797             }
21798
21799             ]
21800         }
21801         ]
21802     },
21803     
21804     content : {
21805         tag: 'tbody',
21806         cn: [
21807         {
21808             tag: 'tr',
21809             cn: [
21810             {
21811                 tag: 'td',
21812                 colspan: '7'
21813             }
21814             ]
21815         }
21816         ]
21817     },
21818     
21819     footer : {
21820         tag: 'tfoot',
21821         cn: [
21822         {
21823             tag: 'tr',
21824             cn: [
21825             {
21826                 tag: 'th',
21827                 colspan: '7',
21828                 cls: 'today'
21829             }
21830                     
21831             ]
21832         }
21833         ]
21834     },
21835     
21836     dates:{
21837         en: {
21838             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21839             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21840             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21841             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21842             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21843             today: "Today"
21844         }
21845     },
21846     
21847     modes: [
21848     {
21849         clsName: 'days',
21850         navFnc: 'Month',
21851         navStep: 1
21852     },
21853     {
21854         clsName: 'months',
21855         navFnc: 'FullYear',
21856         navStep: 1
21857     },
21858     {
21859         clsName: 'years',
21860         navFnc: 'FullYear',
21861         navStep: 10
21862     }]
21863 });
21864
21865 Roo.apply(Roo.bootstrap.DateField,  {
21866   
21867     template : {
21868         tag: 'div',
21869         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21870         cn: [
21871         {
21872             tag: 'div',
21873             cls: 'datepicker-days',
21874             cn: [
21875             {
21876                 tag: 'table',
21877                 cls: 'table-condensed',
21878                 cn:[
21879                 Roo.bootstrap.DateField.head,
21880                 {
21881                     tag: 'tbody'
21882                 },
21883                 Roo.bootstrap.DateField.footer
21884                 ]
21885             }
21886             ]
21887         },
21888         {
21889             tag: 'div',
21890             cls: 'datepicker-months',
21891             cn: [
21892             {
21893                 tag: 'table',
21894                 cls: 'table-condensed',
21895                 cn:[
21896                 Roo.bootstrap.DateField.head,
21897                 Roo.bootstrap.DateField.content,
21898                 Roo.bootstrap.DateField.footer
21899                 ]
21900             }
21901             ]
21902         },
21903         {
21904             tag: 'div',
21905             cls: 'datepicker-years',
21906             cn: [
21907             {
21908                 tag: 'table',
21909                 cls: 'table-condensed',
21910                 cn:[
21911                 Roo.bootstrap.DateField.head,
21912                 Roo.bootstrap.DateField.content,
21913                 Roo.bootstrap.DateField.footer
21914                 ]
21915             }
21916             ]
21917         }
21918         ]
21919     }
21920 });
21921
21922  
21923
21924  /*
21925  * - LGPL
21926  *
21927  * TimeField
21928  * 
21929  */
21930
21931 /**
21932  * @class Roo.bootstrap.TimeField
21933  * @extends Roo.bootstrap.Input
21934  * Bootstrap DateField class
21935  * 
21936  * 
21937  * @constructor
21938  * Create a new TimeField
21939  * @param {Object} config The config object
21940  */
21941
21942 Roo.bootstrap.TimeField = function(config){
21943     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21944     this.addEvents({
21945             /**
21946              * @event show
21947              * Fires when this field show.
21948              * @param {Roo.bootstrap.DateField} thisthis
21949              * @param {Mixed} date The date value
21950              */
21951             show : true,
21952             /**
21953              * @event show
21954              * Fires when this field hide.
21955              * @param {Roo.bootstrap.DateField} this
21956              * @param {Mixed} date The date value
21957              */
21958             hide : true,
21959             /**
21960              * @event select
21961              * Fires when select a date.
21962              * @param {Roo.bootstrap.DateField} this
21963              * @param {Mixed} date The date value
21964              */
21965             select : true
21966         });
21967 };
21968
21969 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21970     
21971     /**
21972      * @cfg {String} format
21973      * The default time format string which can be overriden for localization support.  The format must be
21974      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21975      */
21976     format : "H:i",
21977
21978     getAutoCreate : function()
21979     {
21980         this.after = '<i class="fa far fa-clock"></i>';
21981         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
21982         
21983          
21984     },
21985     onRender: function(ct, position)
21986     {
21987         
21988         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21989                 
21990         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
21991         
21992         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21993         
21994         this.pop = this.picker().select('>.datepicker-time',true).first();
21995         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21996         
21997         this.picker().on('mousedown', this.onMousedown, this);
21998         this.picker().on('click', this.onClick, this);
21999         
22000         this.picker().addClass('datepicker-dropdown');
22001     
22002         this.fillTime();
22003         this.update();
22004             
22005         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22006         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22007         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22008         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22009         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22010         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22011
22012     },
22013     
22014     fireKey: function(e){
22015         if (!this.picker().isVisible()){
22016             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22017                 this.show();
22018             }
22019             return;
22020         }
22021
22022         e.preventDefault();
22023         
22024         switch(e.keyCode){
22025             case 27: // escape
22026                 this.hide();
22027                 break;
22028             case 37: // left
22029             case 39: // right
22030                 this.onTogglePeriod();
22031                 break;
22032             case 38: // up
22033                 this.onIncrementMinutes();
22034                 break;
22035             case 40: // down
22036                 this.onDecrementMinutes();
22037                 break;
22038             case 13: // enter
22039             case 9: // tab
22040                 this.setTime();
22041                 break;
22042         }
22043     },
22044     
22045     onClick: function(e) {
22046         e.stopPropagation();
22047         e.preventDefault();
22048     },
22049     
22050     picker : function()
22051     {
22052         return this.pickerEl;
22053     },
22054     
22055     fillTime: function()
22056     {    
22057         var time = this.pop.select('tbody', true).first();
22058         
22059         time.dom.innerHTML = '';
22060         
22061         time.createChild({
22062             tag: 'tr',
22063             cn: [
22064                 {
22065                     tag: 'td',
22066                     cn: [
22067                         {
22068                             tag: 'a',
22069                             href: '#',
22070                             cls: 'btn',
22071                             cn: [
22072                                 {
22073                                     tag: 'i',
22074                                     cls: 'hours-up fa fas fa-chevron-up'
22075                                 }
22076                             ]
22077                         } 
22078                     ]
22079                 },
22080                 {
22081                     tag: 'td',
22082                     cls: 'separator'
22083                 },
22084                 {
22085                     tag: 'td',
22086                     cn: [
22087                         {
22088                             tag: 'a',
22089                             href: '#',
22090                             cls: 'btn',
22091                             cn: [
22092                                 {
22093                                     tag: 'i',
22094                                     cls: 'minutes-up fa fas fa-chevron-up'
22095                                 }
22096                             ]
22097                         }
22098                     ]
22099                 },
22100                 {
22101                     tag: 'td',
22102                     cls: 'separator'
22103                 }
22104             ]
22105         });
22106         
22107         time.createChild({
22108             tag: 'tr',
22109             cn: [
22110                 {
22111                     tag: 'td',
22112                     cn: [
22113                         {
22114                             tag: 'span',
22115                             cls: 'timepicker-hour',
22116                             html: '00'
22117                         }  
22118                     ]
22119                 },
22120                 {
22121                     tag: 'td',
22122                     cls: 'separator',
22123                     html: ':'
22124                 },
22125                 {
22126                     tag: 'td',
22127                     cn: [
22128                         {
22129                             tag: 'span',
22130                             cls: 'timepicker-minute',
22131                             html: '00'
22132                         }  
22133                     ]
22134                 },
22135                 {
22136                     tag: 'td',
22137                     cls: 'separator'
22138                 },
22139                 {
22140                     tag: 'td',
22141                     cn: [
22142                         {
22143                             tag: 'button',
22144                             type: 'button',
22145                             cls: 'btn btn-primary period',
22146                             html: 'AM'
22147                             
22148                         }
22149                     ]
22150                 }
22151             ]
22152         });
22153         
22154         time.createChild({
22155             tag: 'tr',
22156             cn: [
22157                 {
22158                     tag: 'td',
22159                     cn: [
22160                         {
22161                             tag: 'a',
22162                             href: '#',
22163                             cls: 'btn',
22164                             cn: [
22165                                 {
22166                                     tag: 'span',
22167                                     cls: 'hours-down fa fas fa-chevron-down'
22168                                 }
22169                             ]
22170                         }
22171                     ]
22172                 },
22173                 {
22174                     tag: 'td',
22175                     cls: 'separator'
22176                 },
22177                 {
22178                     tag: 'td',
22179                     cn: [
22180                         {
22181                             tag: 'a',
22182                             href: '#',
22183                             cls: 'btn',
22184                             cn: [
22185                                 {
22186                                     tag: 'span',
22187                                     cls: 'minutes-down fa fas fa-chevron-down'
22188                                 }
22189                             ]
22190                         }
22191                     ]
22192                 },
22193                 {
22194                     tag: 'td',
22195                     cls: 'separator'
22196                 }
22197             ]
22198         });
22199         
22200     },
22201     
22202     update: function()
22203     {
22204         
22205         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22206         
22207         this.fill();
22208     },
22209     
22210     fill: function() 
22211     {
22212         var hours = this.time.getHours();
22213         var minutes = this.time.getMinutes();
22214         var period = 'AM';
22215         
22216         if(hours > 11){
22217             period = 'PM';
22218         }
22219         
22220         if(hours == 0){
22221             hours = 12;
22222         }
22223         
22224         
22225         if(hours > 12){
22226             hours = hours - 12;
22227         }
22228         
22229         if(hours < 10){
22230             hours = '0' + hours;
22231         }
22232         
22233         if(minutes < 10){
22234             minutes = '0' + minutes;
22235         }
22236         
22237         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22238         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22239         this.pop.select('button', true).first().dom.innerHTML = period;
22240         
22241     },
22242     
22243     place: function()
22244     {   
22245         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22246         
22247         var cls = ['bottom'];
22248         
22249         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22250             cls.pop();
22251             cls.push('top');
22252         }
22253         
22254         cls.push('right');
22255         
22256         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22257             cls.pop();
22258             cls.push('left');
22259         }
22260         //this.picker().setXY(20000,20000);
22261         this.picker().addClass(cls.join('-'));
22262         
22263         var _this = this;
22264         
22265         Roo.each(cls, function(c){
22266             if(c == 'bottom'){
22267                 (function() {
22268                  //  
22269                 }).defer(200);
22270                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22271                 //_this.picker().setTop(_this.inputEl().getHeight());
22272                 return;
22273             }
22274             if(c == 'top'){
22275                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22276                 
22277                 //_this.picker().setTop(0 - _this.picker().getHeight());
22278                 return;
22279             }
22280             /*
22281             if(c == 'left'){
22282                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22283                 return;
22284             }
22285             if(c == 'right'){
22286                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22287                 return;
22288             }
22289             */
22290         });
22291         
22292     },
22293   
22294     onFocus : function()
22295     {
22296         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22297         this.show();
22298     },
22299     
22300     onBlur : function()
22301     {
22302         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22303         this.hide();
22304     },
22305     
22306     show : function()
22307     {
22308         this.picker().show();
22309         this.pop.show();
22310         this.update();
22311         this.place();
22312         
22313         this.fireEvent('show', this, this.date);
22314     },
22315     
22316     hide : function()
22317     {
22318         this.picker().hide();
22319         this.pop.hide();
22320         
22321         this.fireEvent('hide', this, this.date);
22322     },
22323     
22324     setTime : function()
22325     {
22326         this.hide();
22327         this.setValue(this.time.format(this.format));
22328         
22329         this.fireEvent('select', this, this.date);
22330         
22331         
22332     },
22333     
22334     onMousedown: function(e){
22335         e.stopPropagation();
22336         e.preventDefault();
22337     },
22338     
22339     onIncrementHours: function()
22340     {
22341         Roo.log('onIncrementHours');
22342         this.time = this.time.add(Date.HOUR, 1);
22343         this.update();
22344         
22345     },
22346     
22347     onDecrementHours: function()
22348     {
22349         Roo.log('onDecrementHours');
22350         this.time = this.time.add(Date.HOUR, -1);
22351         this.update();
22352     },
22353     
22354     onIncrementMinutes: function()
22355     {
22356         Roo.log('onIncrementMinutes');
22357         this.time = this.time.add(Date.MINUTE, 1);
22358         this.update();
22359     },
22360     
22361     onDecrementMinutes: function()
22362     {
22363         Roo.log('onDecrementMinutes');
22364         this.time = this.time.add(Date.MINUTE, -1);
22365         this.update();
22366     },
22367     
22368     onTogglePeriod: function()
22369     {
22370         Roo.log('onTogglePeriod');
22371         this.time = this.time.add(Date.HOUR, 12);
22372         this.update();
22373     }
22374     
22375    
22376 });
22377  
22378
22379 Roo.apply(Roo.bootstrap.TimeField,  {
22380   
22381     template : {
22382         tag: 'div',
22383         cls: 'datepicker dropdown-menu',
22384         cn: [
22385             {
22386                 tag: 'div',
22387                 cls: 'datepicker-time',
22388                 cn: [
22389                 {
22390                     tag: 'table',
22391                     cls: 'table-condensed',
22392                     cn:[
22393                         {
22394                             tag: 'tbody',
22395                             cn: [
22396                                 {
22397                                     tag: 'tr',
22398                                     cn: [
22399                                     {
22400                                         tag: 'td',
22401                                         colspan: '7'
22402                                     }
22403                                     ]
22404                                 }
22405                             ]
22406                         },
22407                         {
22408                             tag: 'tfoot',
22409                             cn: [
22410                                 {
22411                                     tag: 'tr',
22412                                     cn: [
22413                                     {
22414                                         tag: 'th',
22415                                         colspan: '7',
22416                                         cls: '',
22417                                         cn: [
22418                                             {
22419                                                 tag: 'button',
22420                                                 cls: 'btn btn-info ok',
22421                                                 html: 'OK'
22422                                             }
22423                                         ]
22424                                     }
22425                     
22426                                     ]
22427                                 }
22428                             ]
22429                         }
22430                     ]
22431                 }
22432                 ]
22433             }
22434         ]
22435     }
22436 });
22437
22438  
22439
22440  /*
22441  * - LGPL
22442  *
22443  * MonthField
22444  * 
22445  */
22446
22447 /**
22448  * @class Roo.bootstrap.MonthField
22449  * @extends Roo.bootstrap.Input
22450  * Bootstrap MonthField class
22451  * 
22452  * @cfg {String} language default en
22453  * 
22454  * @constructor
22455  * Create a new MonthField
22456  * @param {Object} config The config object
22457  */
22458
22459 Roo.bootstrap.MonthField = function(config){
22460     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22461     
22462     this.addEvents({
22463         /**
22464          * @event show
22465          * Fires when this field show.
22466          * @param {Roo.bootstrap.MonthField} this
22467          * @param {Mixed} date The date value
22468          */
22469         show : true,
22470         /**
22471          * @event show
22472          * Fires when this field hide.
22473          * @param {Roo.bootstrap.MonthField} this
22474          * @param {Mixed} date The date value
22475          */
22476         hide : true,
22477         /**
22478          * @event select
22479          * Fires when select a date.
22480          * @param {Roo.bootstrap.MonthField} this
22481          * @param {String} oldvalue The old value
22482          * @param {String} newvalue The new value
22483          */
22484         select : true
22485     });
22486 };
22487
22488 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22489     
22490     onRender: function(ct, position)
22491     {
22492         
22493         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22494         
22495         this.language = this.language || 'en';
22496         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22497         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22498         
22499         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22500         this.isInline = false;
22501         this.isInput = true;
22502         this.component = this.el.select('.add-on', true).first() || false;
22503         this.component = (this.component && this.component.length === 0) ? false : this.component;
22504         this.hasInput = this.component && this.inputEL().length;
22505         
22506         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22507         
22508         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22509         
22510         this.picker().on('mousedown', this.onMousedown, this);
22511         this.picker().on('click', this.onClick, this);
22512         
22513         this.picker().addClass('datepicker-dropdown');
22514         
22515         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22516             v.setStyle('width', '189px');
22517         });
22518         
22519         this.fillMonths();
22520         
22521         this.update();
22522         
22523         if(this.isInline) {
22524             this.show();
22525         }
22526         
22527     },
22528     
22529     setValue: function(v, suppressEvent)
22530     {   
22531         var o = this.getValue();
22532         
22533         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22534         
22535         this.update();
22536
22537         if(suppressEvent !== true){
22538             this.fireEvent('select', this, o, v);
22539         }
22540         
22541     },
22542     
22543     getValue: function()
22544     {
22545         return this.value;
22546     },
22547     
22548     onClick: function(e) 
22549     {
22550         e.stopPropagation();
22551         e.preventDefault();
22552         
22553         var target = e.getTarget();
22554         
22555         if(target.nodeName.toLowerCase() === 'i'){
22556             target = Roo.get(target).dom.parentNode;
22557         }
22558         
22559         var nodeName = target.nodeName;
22560         var className = target.className;
22561         var html = target.innerHTML;
22562         
22563         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22564             return;
22565         }
22566         
22567         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22568         
22569         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22570         
22571         this.hide();
22572                         
22573     },
22574     
22575     picker : function()
22576     {
22577         return this.pickerEl;
22578     },
22579     
22580     fillMonths: function()
22581     {    
22582         var i = 0;
22583         var months = this.picker().select('>.datepicker-months td', true).first();
22584         
22585         months.dom.innerHTML = '';
22586         
22587         while (i < 12) {
22588             var month = {
22589                 tag: 'span',
22590                 cls: 'month',
22591                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22592             };
22593             
22594             months.createChild(month);
22595         }
22596         
22597     },
22598     
22599     update: function()
22600     {
22601         var _this = this;
22602         
22603         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22604             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22605         }
22606         
22607         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22608             e.removeClass('active');
22609             
22610             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22611                 e.addClass('active');
22612             }
22613         })
22614     },
22615     
22616     place: function()
22617     {
22618         if(this.isInline) {
22619             return;
22620         }
22621         
22622         this.picker().removeClass(['bottom', 'top']);
22623         
22624         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22625             /*
22626              * place to the top of element!
22627              *
22628              */
22629             
22630             this.picker().addClass('top');
22631             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22632             
22633             return;
22634         }
22635         
22636         this.picker().addClass('bottom');
22637         
22638         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22639     },
22640     
22641     onFocus : function()
22642     {
22643         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22644         this.show();
22645     },
22646     
22647     onBlur : function()
22648     {
22649         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22650         
22651         var d = this.inputEl().getValue();
22652         
22653         this.setValue(d);
22654                 
22655         this.hide();
22656     },
22657     
22658     show : function()
22659     {
22660         this.picker().show();
22661         this.picker().select('>.datepicker-months', true).first().show();
22662         this.update();
22663         this.place();
22664         
22665         this.fireEvent('show', this, this.date);
22666     },
22667     
22668     hide : function()
22669     {
22670         if(this.isInline) {
22671             return;
22672         }
22673         this.picker().hide();
22674         this.fireEvent('hide', this, this.date);
22675         
22676     },
22677     
22678     onMousedown: function(e)
22679     {
22680         e.stopPropagation();
22681         e.preventDefault();
22682     },
22683     
22684     keyup: function(e)
22685     {
22686         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22687         this.update();
22688     },
22689
22690     fireKey: function(e)
22691     {
22692         if (!this.picker().isVisible()){
22693             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22694                 this.show();
22695             }
22696             return;
22697         }
22698         
22699         var dir;
22700         
22701         switch(e.keyCode){
22702             case 27: // escape
22703                 this.hide();
22704                 e.preventDefault();
22705                 break;
22706             case 37: // left
22707             case 39: // right
22708                 dir = e.keyCode == 37 ? -1 : 1;
22709                 
22710                 this.vIndex = this.vIndex + dir;
22711                 
22712                 if(this.vIndex < 0){
22713                     this.vIndex = 0;
22714                 }
22715                 
22716                 if(this.vIndex > 11){
22717                     this.vIndex = 11;
22718                 }
22719                 
22720                 if(isNaN(this.vIndex)){
22721                     this.vIndex = 0;
22722                 }
22723                 
22724                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22725                 
22726                 break;
22727             case 38: // up
22728             case 40: // down
22729                 
22730                 dir = e.keyCode == 38 ? -1 : 1;
22731                 
22732                 this.vIndex = this.vIndex + dir * 4;
22733                 
22734                 if(this.vIndex < 0){
22735                     this.vIndex = 0;
22736                 }
22737                 
22738                 if(this.vIndex > 11){
22739                     this.vIndex = 11;
22740                 }
22741                 
22742                 if(isNaN(this.vIndex)){
22743                     this.vIndex = 0;
22744                 }
22745                 
22746                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22747                 break;
22748                 
22749             case 13: // enter
22750                 
22751                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22752                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22753                 }
22754                 
22755                 this.hide();
22756                 e.preventDefault();
22757                 break;
22758             case 9: // tab
22759                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22760                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22761                 }
22762                 this.hide();
22763                 break;
22764             case 16: // shift
22765             case 17: // ctrl
22766             case 18: // alt
22767                 break;
22768             default :
22769                 this.hide();
22770                 
22771         }
22772     },
22773     
22774     remove: function() 
22775     {
22776         this.picker().remove();
22777     }
22778    
22779 });
22780
22781 Roo.apply(Roo.bootstrap.MonthField,  {
22782     
22783     content : {
22784         tag: 'tbody',
22785         cn: [
22786         {
22787             tag: 'tr',
22788             cn: [
22789             {
22790                 tag: 'td',
22791                 colspan: '7'
22792             }
22793             ]
22794         }
22795         ]
22796     },
22797     
22798     dates:{
22799         en: {
22800             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22801             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22802         }
22803     }
22804 });
22805
22806 Roo.apply(Roo.bootstrap.MonthField,  {
22807   
22808     template : {
22809         tag: 'div',
22810         cls: 'datepicker dropdown-menu roo-dynamic',
22811         cn: [
22812             {
22813                 tag: 'div',
22814                 cls: 'datepicker-months',
22815                 cn: [
22816                 {
22817                     tag: 'table',
22818                     cls: 'table-condensed',
22819                     cn:[
22820                         Roo.bootstrap.DateField.content
22821                     ]
22822                 }
22823                 ]
22824             }
22825         ]
22826     }
22827 });
22828
22829  
22830
22831  
22832  /*
22833  * - LGPL
22834  *
22835  * CheckBox
22836  * 
22837  */
22838
22839 /**
22840  * @class Roo.bootstrap.CheckBox
22841  * @extends Roo.bootstrap.Input
22842  * Bootstrap CheckBox class
22843  * 
22844  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22845  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22846  * @cfg {String} boxLabel The text that appears beside the checkbox
22847  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22848  * @cfg {Boolean} checked initnal the element
22849  * @cfg {Boolean} inline inline the element (default false)
22850  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22851  * @cfg {String} tooltip label tooltip
22852  * 
22853  * @constructor
22854  * Create a new CheckBox
22855  * @param {Object} config The config object
22856  */
22857
22858 Roo.bootstrap.CheckBox = function(config){
22859     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22860    
22861     this.addEvents({
22862         /**
22863         * @event check
22864         * Fires when the element is checked or unchecked.
22865         * @param {Roo.bootstrap.CheckBox} this This input
22866         * @param {Boolean} checked The new checked value
22867         */
22868        check : true,
22869        /**
22870         * @event click
22871         * Fires when the element is click.
22872         * @param {Roo.bootstrap.CheckBox} this This input
22873         */
22874        click : true
22875     });
22876     
22877 };
22878
22879 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22880   
22881     inputType: 'checkbox',
22882     inputValue: 1,
22883     valueOff: 0,
22884     boxLabel: false,
22885     checked: false,
22886     weight : false,
22887     inline: false,
22888     tooltip : '',
22889     
22890     // checkbox success does not make any sense really.. 
22891     invalidClass : "",
22892     validClass : "",
22893     
22894     
22895     getAutoCreate : function()
22896     {
22897         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22898         
22899         var id = Roo.id();
22900         
22901         var cfg = {};
22902         
22903         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22904         
22905         if(this.inline){
22906             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22907         }
22908         
22909         var input =  {
22910             tag: 'input',
22911             id : id,
22912             type : this.inputType,
22913             value : this.inputValue,
22914             cls : 'roo-' + this.inputType, //'form-box',
22915             placeholder : this.placeholder || ''
22916             
22917         };
22918         
22919         if(this.inputType != 'radio'){
22920             var hidden =  {
22921                 tag: 'input',
22922                 type : 'hidden',
22923                 cls : 'roo-hidden-value',
22924                 value : this.checked ? this.inputValue : this.valueOff
22925             };
22926         }
22927         
22928             
22929         if (this.weight) { // Validity check?
22930             cfg.cls += " " + this.inputType + "-" + this.weight;
22931         }
22932         
22933         if (this.disabled) {
22934             input.disabled=true;
22935         }
22936         
22937         if(this.checked){
22938             input.checked = this.checked;
22939         }
22940         
22941         if (this.name) {
22942             
22943             input.name = this.name;
22944             
22945             if(this.inputType != 'radio'){
22946                 hidden.name = this.name;
22947                 input.name = '_hidden_' + this.name;
22948             }
22949         }
22950         
22951         if (this.size) {
22952             input.cls += ' input-' + this.size;
22953         }
22954         
22955         var settings=this;
22956         
22957         ['xs','sm','md','lg'].map(function(size){
22958             if (settings[size]) {
22959                 cfg.cls += ' col-' + size + '-' + settings[size];
22960             }
22961         });
22962         
22963         var inputblock = input;
22964          
22965         if (this.before || this.after) {
22966             
22967             inputblock = {
22968                 cls : 'input-group',
22969                 cn :  [] 
22970             };
22971             
22972             if (this.before) {
22973                 inputblock.cn.push({
22974                     tag :'span',
22975                     cls : 'input-group-addon',
22976                     html : this.before
22977                 });
22978             }
22979             
22980             inputblock.cn.push(input);
22981             
22982             if(this.inputType != 'radio'){
22983                 inputblock.cn.push(hidden);
22984             }
22985             
22986             if (this.after) {
22987                 inputblock.cn.push({
22988                     tag :'span',
22989                     cls : 'input-group-addon',
22990                     html : this.after
22991                 });
22992             }
22993             
22994         }
22995         var boxLabelCfg = false;
22996         
22997         if(this.boxLabel){
22998            
22999             boxLabelCfg = {
23000                 tag: 'label',
23001                 //'for': id, // box label is handled by onclick - so no for...
23002                 cls: 'box-label',
23003                 html: this.boxLabel
23004             };
23005             if(this.tooltip){
23006                 boxLabelCfg.tooltip = this.tooltip;
23007             }
23008              
23009         }
23010         
23011         
23012         if (align ==='left' && this.fieldLabel.length) {
23013 //                Roo.log("left and has label");
23014             cfg.cn = [
23015                 {
23016                     tag: 'label',
23017                     'for' :  id,
23018                     cls : 'control-label',
23019                     html : this.fieldLabel
23020                 },
23021                 {
23022                     cls : "", 
23023                     cn: [
23024                         inputblock
23025                     ]
23026                 }
23027             ];
23028             
23029             if (boxLabelCfg) {
23030                 cfg.cn[1].cn.push(boxLabelCfg);
23031             }
23032             
23033             if(this.labelWidth > 12){
23034                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23035             }
23036             
23037             if(this.labelWidth < 13 && this.labelmd == 0){
23038                 this.labelmd = this.labelWidth;
23039             }
23040             
23041             if(this.labellg > 0){
23042                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23043                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23044             }
23045             
23046             if(this.labelmd > 0){
23047                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23048                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23049             }
23050             
23051             if(this.labelsm > 0){
23052                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23053                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23054             }
23055             
23056             if(this.labelxs > 0){
23057                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23058                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23059             }
23060             
23061         } else if ( this.fieldLabel.length) {
23062 //                Roo.log(" label");
23063                 cfg.cn = [
23064                    
23065                     {
23066                         tag: this.boxLabel ? 'span' : 'label',
23067                         'for': id,
23068                         cls: 'control-label box-input-label',
23069                         //cls : 'input-group-addon',
23070                         html : this.fieldLabel
23071                     },
23072                     
23073                     inputblock
23074                     
23075                 ];
23076                 if (boxLabelCfg) {
23077                     cfg.cn.push(boxLabelCfg);
23078                 }
23079
23080         } else {
23081             
23082 //                Roo.log(" no label && no align");
23083                 cfg.cn = [  inputblock ] ;
23084                 if (boxLabelCfg) {
23085                     cfg.cn.push(boxLabelCfg);
23086                 }
23087
23088                 
23089         }
23090         
23091        
23092         
23093         if(this.inputType != 'radio'){
23094             cfg.cn.push(hidden);
23095         }
23096         
23097         return cfg;
23098         
23099     },
23100     
23101     /**
23102      * return the real input element.
23103      */
23104     inputEl: function ()
23105     {
23106         return this.el.select('input.roo-' + this.inputType,true).first();
23107     },
23108     hiddenEl: function ()
23109     {
23110         return this.el.select('input.roo-hidden-value',true).first();
23111     },
23112     
23113     labelEl: function()
23114     {
23115         return this.el.select('label.control-label',true).first();
23116     },
23117     /* depricated... */
23118     
23119     label: function()
23120     {
23121         return this.labelEl();
23122     },
23123     
23124     boxLabelEl: function()
23125     {
23126         return this.el.select('label.box-label',true).first();
23127     },
23128     
23129     initEvents : function()
23130     {
23131 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23132         
23133         this.inputEl().on('click', this.onClick,  this);
23134         
23135         if (this.boxLabel) { 
23136             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23137         }
23138         
23139         this.startValue = this.getValue();
23140         
23141         if(this.groupId){
23142             Roo.bootstrap.CheckBox.register(this);
23143         }
23144     },
23145     
23146     onClick : function(e)
23147     {   
23148         if(this.fireEvent('click', this, e) !== false){
23149             this.setChecked(!this.checked);
23150         }
23151         
23152     },
23153     
23154     setChecked : function(state,suppressEvent)
23155     {
23156         this.startValue = this.getValue();
23157
23158         if(this.inputType == 'radio'){
23159             
23160             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23161                 e.dom.checked = false;
23162             });
23163             
23164             this.inputEl().dom.checked = true;
23165             
23166             this.inputEl().dom.value = this.inputValue;
23167             
23168             if(suppressEvent !== true){
23169                 this.fireEvent('check', this, true);
23170             }
23171             
23172             this.validate();
23173             
23174             return;
23175         }
23176         
23177         this.checked = state;
23178         
23179         this.inputEl().dom.checked = state;
23180         
23181         
23182         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23183         
23184         if(suppressEvent !== true){
23185             this.fireEvent('check', this, state);
23186         }
23187         
23188         this.validate();
23189     },
23190     
23191     getValue : function()
23192     {
23193         if(this.inputType == 'radio'){
23194             return this.getGroupValue();
23195         }
23196         
23197         return this.hiddenEl().dom.value;
23198         
23199     },
23200     
23201     getGroupValue : function()
23202     {
23203         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23204             return '';
23205         }
23206         
23207         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23208     },
23209     
23210     setValue : function(v,suppressEvent)
23211     {
23212         if(this.inputType == 'radio'){
23213             this.setGroupValue(v, suppressEvent);
23214             return;
23215         }
23216         
23217         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23218         
23219         this.validate();
23220     },
23221     
23222     setGroupValue : function(v, suppressEvent)
23223     {
23224         this.startValue = this.getValue();
23225         
23226         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23227             e.dom.checked = false;
23228             
23229             if(e.dom.value == v){
23230                 e.dom.checked = true;
23231             }
23232         });
23233         
23234         if(suppressEvent !== true){
23235             this.fireEvent('check', this, true);
23236         }
23237
23238         this.validate();
23239         
23240         return;
23241     },
23242     
23243     validate : function()
23244     {
23245         if(this.getVisibilityEl().hasClass('hidden')){
23246             return true;
23247         }
23248         
23249         if(
23250                 this.disabled || 
23251                 (this.inputType == 'radio' && this.validateRadio()) ||
23252                 (this.inputType == 'checkbox' && this.validateCheckbox())
23253         ){
23254             this.markValid();
23255             return true;
23256         }
23257         
23258         this.markInvalid();
23259         return false;
23260     },
23261     
23262     validateRadio : function()
23263     {
23264         if(this.getVisibilityEl().hasClass('hidden')){
23265             return true;
23266         }
23267         
23268         if(this.allowBlank){
23269             return true;
23270         }
23271         
23272         var valid = false;
23273         
23274         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23275             if(!e.dom.checked){
23276                 return;
23277             }
23278             
23279             valid = true;
23280             
23281             return false;
23282         });
23283         
23284         return valid;
23285     },
23286     
23287     validateCheckbox : function()
23288     {
23289         if(!this.groupId){
23290             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23291             //return (this.getValue() == this.inputValue) ? true : false;
23292         }
23293         
23294         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23295         
23296         if(!group){
23297             return false;
23298         }
23299         
23300         var r = false;
23301         
23302         for(var i in group){
23303             if(group[i].el.isVisible(true)){
23304                 r = false;
23305                 break;
23306             }
23307             
23308             r = true;
23309         }
23310         
23311         for(var i in group){
23312             if(r){
23313                 break;
23314             }
23315             
23316             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23317         }
23318         
23319         return r;
23320     },
23321     
23322     /**
23323      * Mark this field as valid
23324      */
23325     markValid : function()
23326     {
23327         var _this = this;
23328         
23329         this.fireEvent('valid', this);
23330         
23331         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23332         
23333         if(this.groupId){
23334             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23335         }
23336         
23337         if(label){
23338             label.markValid();
23339         }
23340
23341         if(this.inputType == 'radio'){
23342             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23343                 var fg = e.findParent('.form-group', false, true);
23344                 if (Roo.bootstrap.version == 3) {
23345                     fg.removeClass([_this.invalidClass, _this.validClass]);
23346                     fg.addClass(_this.validClass);
23347                 } else {
23348                     fg.removeClass(['is-valid', 'is-invalid']);
23349                     fg.addClass('is-valid');
23350                 }
23351             });
23352             
23353             return;
23354         }
23355
23356         if(!this.groupId){
23357             var fg = this.el.findParent('.form-group', false, true);
23358             if (Roo.bootstrap.version == 3) {
23359                 fg.removeClass([this.invalidClass, this.validClass]);
23360                 fg.addClass(this.validClass);
23361             } else {
23362                 fg.removeClass(['is-valid', 'is-invalid']);
23363                 fg.addClass('is-valid');
23364             }
23365             return;
23366         }
23367         
23368         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23369         
23370         if(!group){
23371             return;
23372         }
23373         
23374         for(var i in group){
23375             var fg = group[i].el.findParent('.form-group', false, true);
23376             if (Roo.bootstrap.version == 3) {
23377                 fg.removeClass([this.invalidClass, this.validClass]);
23378                 fg.addClass(this.validClass);
23379             } else {
23380                 fg.removeClass(['is-valid', 'is-invalid']);
23381                 fg.addClass('is-valid');
23382             }
23383         }
23384     },
23385     
23386      /**
23387      * Mark this field as invalid
23388      * @param {String} msg The validation message
23389      */
23390     markInvalid : function(msg)
23391     {
23392         if(this.allowBlank){
23393             return;
23394         }
23395         
23396         var _this = this;
23397         
23398         this.fireEvent('invalid', this, msg);
23399         
23400         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23401         
23402         if(this.groupId){
23403             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23404         }
23405         
23406         if(label){
23407             label.markInvalid();
23408         }
23409             
23410         if(this.inputType == 'radio'){
23411             
23412             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23413                 var fg = e.findParent('.form-group', false, true);
23414                 if (Roo.bootstrap.version == 3) {
23415                     fg.removeClass([_this.invalidClass, _this.validClass]);
23416                     fg.addClass(_this.invalidClass);
23417                 } else {
23418                     fg.removeClass(['is-invalid', 'is-valid']);
23419                     fg.addClass('is-invalid');
23420                 }
23421             });
23422             
23423             return;
23424         }
23425         
23426         if(!this.groupId){
23427             var fg = this.el.findParent('.form-group', false, true);
23428             if (Roo.bootstrap.version == 3) {
23429                 fg.removeClass([_this.invalidClass, _this.validClass]);
23430                 fg.addClass(_this.invalidClass);
23431             } else {
23432                 fg.removeClass(['is-invalid', 'is-valid']);
23433                 fg.addClass('is-invalid');
23434             }
23435             return;
23436         }
23437         
23438         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23439         
23440         if(!group){
23441             return;
23442         }
23443         
23444         for(var i in group){
23445             var fg = group[i].el.findParent('.form-group', false, true);
23446             if (Roo.bootstrap.version == 3) {
23447                 fg.removeClass([_this.invalidClass, _this.validClass]);
23448                 fg.addClass(_this.invalidClass);
23449             } else {
23450                 fg.removeClass(['is-invalid', 'is-valid']);
23451                 fg.addClass('is-invalid');
23452             }
23453         }
23454         
23455     },
23456     
23457     clearInvalid : function()
23458     {
23459         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23460         
23461         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23462         
23463         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23464         
23465         if (label && label.iconEl) {
23466             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23467             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23468         }
23469     },
23470     
23471     disable : function()
23472     {
23473         if(this.inputType != 'radio'){
23474             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23475             return;
23476         }
23477         
23478         var _this = this;
23479         
23480         if(this.rendered){
23481             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23482                 _this.getActionEl().addClass(this.disabledClass);
23483                 e.dom.disabled = true;
23484             });
23485         }
23486         
23487         this.disabled = true;
23488         this.fireEvent("disable", this);
23489         return this;
23490     },
23491
23492     enable : function()
23493     {
23494         if(this.inputType != 'radio'){
23495             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23496             return;
23497         }
23498         
23499         var _this = this;
23500         
23501         if(this.rendered){
23502             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23503                 _this.getActionEl().removeClass(this.disabledClass);
23504                 e.dom.disabled = false;
23505             });
23506         }
23507         
23508         this.disabled = false;
23509         this.fireEvent("enable", this);
23510         return this;
23511     },
23512     
23513     setBoxLabel : function(v)
23514     {
23515         this.boxLabel = v;
23516         
23517         if(this.rendered){
23518             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23519         }
23520     }
23521
23522 });
23523
23524 Roo.apply(Roo.bootstrap.CheckBox, {
23525     
23526     groups: {},
23527     
23528      /**
23529     * register a CheckBox Group
23530     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23531     */
23532     register : function(checkbox)
23533     {
23534         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23535             this.groups[checkbox.groupId] = {};
23536         }
23537         
23538         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23539             return;
23540         }
23541         
23542         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23543         
23544     },
23545     /**
23546     * fetch a CheckBox Group based on the group ID
23547     * @param {string} the group ID
23548     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23549     */
23550     get: function(groupId) {
23551         if (typeof(this.groups[groupId]) == 'undefined') {
23552             return false;
23553         }
23554         
23555         return this.groups[groupId] ;
23556     }
23557     
23558     
23559 });
23560 /*
23561  * - LGPL
23562  *
23563  * RadioItem
23564  * 
23565  */
23566
23567 /**
23568  * @class Roo.bootstrap.Radio
23569  * @extends Roo.bootstrap.Component
23570  * Bootstrap Radio class
23571  * @cfg {String} boxLabel - the label associated
23572  * @cfg {String} value - the value of radio
23573  * 
23574  * @constructor
23575  * Create a new Radio
23576  * @param {Object} config The config object
23577  */
23578 Roo.bootstrap.Radio = function(config){
23579     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23580     
23581 };
23582
23583 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23584     
23585     boxLabel : '',
23586     
23587     value : '',
23588     
23589     getAutoCreate : function()
23590     {
23591         var cfg = {
23592             tag : 'div',
23593             cls : 'form-group radio',
23594             cn : [
23595                 {
23596                     tag : 'label',
23597                     cls : 'box-label',
23598                     html : this.boxLabel
23599                 }
23600             ]
23601         };
23602         
23603         return cfg;
23604     },
23605     
23606     initEvents : function() 
23607     {
23608         this.parent().register(this);
23609         
23610         this.el.on('click', this.onClick, this);
23611         
23612     },
23613     
23614     onClick : function(e)
23615     {
23616         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23617             this.setChecked(true);
23618         }
23619     },
23620     
23621     setChecked : function(state, suppressEvent)
23622     {
23623         this.parent().setValue(this.value, suppressEvent);
23624         
23625     },
23626     
23627     setBoxLabel : function(v)
23628     {
23629         this.boxLabel = v;
23630         
23631         if(this.rendered){
23632             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23633         }
23634     }
23635     
23636 });
23637  
23638
23639  /*
23640  * - LGPL
23641  *
23642  * Input
23643  * 
23644  */
23645
23646 /**
23647  * @class Roo.bootstrap.SecurePass
23648  * @extends Roo.bootstrap.Input
23649  * Bootstrap SecurePass class
23650  *
23651  * 
23652  * @constructor
23653  * Create a new SecurePass
23654  * @param {Object} config The config object
23655  */
23656  
23657 Roo.bootstrap.SecurePass = function (config) {
23658     // these go here, so the translation tool can replace them..
23659     this.errors = {
23660         PwdEmpty: "Please type a password, and then retype it to confirm.",
23661         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23662         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23663         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23664         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23665         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23666         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23667         TooWeak: "Your password is Too Weak."
23668     },
23669     this.meterLabel = "Password strength:";
23670     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23671     this.meterClass = [
23672         "roo-password-meter-tooweak", 
23673         "roo-password-meter-weak", 
23674         "roo-password-meter-medium", 
23675         "roo-password-meter-strong", 
23676         "roo-password-meter-grey"
23677     ];
23678     
23679     this.errors = {};
23680     
23681     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23682 }
23683
23684 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23685     /**
23686      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23687      * {
23688      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23689      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23690      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23691      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23692      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23693      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23694      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23695      * })
23696      */
23697     // private
23698     
23699     meterWidth: 300,
23700     errorMsg :'',    
23701     errors: false,
23702     imageRoot: '/',
23703     /**
23704      * @cfg {String/Object} Label for the strength meter (defaults to
23705      * 'Password strength:')
23706      */
23707     // private
23708     meterLabel: '',
23709     /**
23710      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23711      * ['Weak', 'Medium', 'Strong'])
23712      */
23713     // private    
23714     pwdStrengths: false,    
23715     // private
23716     strength: 0,
23717     // private
23718     _lastPwd: null,
23719     // private
23720     kCapitalLetter: 0,
23721     kSmallLetter: 1,
23722     kDigit: 2,
23723     kPunctuation: 3,
23724     
23725     insecure: false,
23726     // private
23727     initEvents: function ()
23728     {
23729         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23730
23731         if (this.el.is('input[type=password]') && Roo.isSafari) {
23732             this.el.on('keydown', this.SafariOnKeyDown, this);
23733         }
23734
23735         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23736     },
23737     // private
23738     onRender: function (ct, position)
23739     {
23740         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23741         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23742         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23743
23744         this.trigger.createChild({
23745                    cn: [
23746                     {
23747                     //id: 'PwdMeter',
23748                     tag: 'div',
23749                     cls: 'roo-password-meter-grey col-xs-12',
23750                     style: {
23751                         //width: 0,
23752                         //width: this.meterWidth + 'px'                                                
23753                         }
23754                     },
23755                     {                            
23756                          cls: 'roo-password-meter-text'                          
23757                     }
23758                 ]            
23759         });
23760
23761          
23762         if (this.hideTrigger) {
23763             this.trigger.setDisplayed(false);
23764         }
23765         this.setSize(this.width || '', this.height || '');
23766     },
23767     // private
23768     onDestroy: function ()
23769     {
23770         if (this.trigger) {
23771             this.trigger.removeAllListeners();
23772             this.trigger.remove();
23773         }
23774         if (this.wrap) {
23775             this.wrap.remove();
23776         }
23777         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23778     },
23779     // private
23780     checkStrength: function ()
23781     {
23782         var pwd = this.inputEl().getValue();
23783         if (pwd == this._lastPwd) {
23784             return;
23785         }
23786
23787         var strength;
23788         if (this.ClientSideStrongPassword(pwd)) {
23789             strength = 3;
23790         } else if (this.ClientSideMediumPassword(pwd)) {
23791             strength = 2;
23792         } else if (this.ClientSideWeakPassword(pwd)) {
23793             strength = 1;
23794         } else {
23795             strength = 0;
23796         }
23797         
23798         Roo.log('strength1: ' + strength);
23799         
23800         //var pm = this.trigger.child('div/div/div').dom;
23801         var pm = this.trigger.child('div/div');
23802         pm.removeClass(this.meterClass);
23803         pm.addClass(this.meterClass[strength]);
23804                 
23805         
23806         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23807                 
23808         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23809         
23810         this._lastPwd = pwd;
23811     },
23812     reset: function ()
23813     {
23814         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23815         
23816         this._lastPwd = '';
23817         
23818         var pm = this.trigger.child('div/div');
23819         pm.removeClass(this.meterClass);
23820         pm.addClass('roo-password-meter-grey');        
23821         
23822         
23823         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23824         
23825         pt.innerHTML = '';
23826         this.inputEl().dom.type='password';
23827     },
23828     // private
23829     validateValue: function (value)
23830     {
23831         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23832             return false;
23833         }
23834         if (value.length == 0) {
23835             if (this.allowBlank) {
23836                 this.clearInvalid();
23837                 return true;
23838             }
23839
23840             this.markInvalid(this.errors.PwdEmpty);
23841             this.errorMsg = this.errors.PwdEmpty;
23842             return false;
23843         }
23844         
23845         if(this.insecure){
23846             return true;
23847         }
23848         
23849         if (!value.match(/[\x21-\x7e]+/)) {
23850             this.markInvalid(this.errors.PwdBadChar);
23851             this.errorMsg = this.errors.PwdBadChar;
23852             return false;
23853         }
23854         if (value.length < 6) {
23855             this.markInvalid(this.errors.PwdShort);
23856             this.errorMsg = this.errors.PwdShort;
23857             return false;
23858         }
23859         if (value.length > 16) {
23860             this.markInvalid(this.errors.PwdLong);
23861             this.errorMsg = this.errors.PwdLong;
23862             return false;
23863         }
23864         var strength;
23865         if (this.ClientSideStrongPassword(value)) {
23866             strength = 3;
23867         } else if (this.ClientSideMediumPassword(value)) {
23868             strength = 2;
23869         } else if (this.ClientSideWeakPassword(value)) {
23870             strength = 1;
23871         } else {
23872             strength = 0;
23873         }
23874
23875         
23876         if (strength < 2) {
23877             //this.markInvalid(this.errors.TooWeak);
23878             this.errorMsg = this.errors.TooWeak;
23879             //return false;
23880         }
23881         
23882         
23883         console.log('strength2: ' + strength);
23884         
23885         //var pm = this.trigger.child('div/div/div').dom;
23886         
23887         var pm = this.trigger.child('div/div');
23888         pm.removeClass(this.meterClass);
23889         pm.addClass(this.meterClass[strength]);
23890                 
23891         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23892                 
23893         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23894         
23895         this.errorMsg = ''; 
23896         return true;
23897     },
23898     // private
23899     CharacterSetChecks: function (type)
23900     {
23901         this.type = type;
23902         this.fResult = false;
23903     },
23904     // private
23905     isctype: function (character, type)
23906     {
23907         switch (type) {  
23908             case this.kCapitalLetter:
23909                 if (character >= 'A' && character <= 'Z') {
23910                     return true;
23911                 }
23912                 break;
23913             
23914             case this.kSmallLetter:
23915                 if (character >= 'a' && character <= 'z') {
23916                     return true;
23917                 }
23918                 break;
23919             
23920             case this.kDigit:
23921                 if (character >= '0' && character <= '9') {
23922                     return true;
23923                 }
23924                 break;
23925             
23926             case this.kPunctuation:
23927                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23928                     return true;
23929                 }
23930                 break;
23931             
23932             default:
23933                 return false;
23934         }
23935
23936     },
23937     // private
23938     IsLongEnough: function (pwd, size)
23939     {
23940         return !(pwd == null || isNaN(size) || pwd.length < size);
23941     },
23942     // private
23943     SpansEnoughCharacterSets: function (word, nb)
23944     {
23945         if (!this.IsLongEnough(word, nb))
23946         {
23947             return false;
23948         }
23949
23950         var characterSetChecks = new Array(
23951             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23952             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23953         );
23954         
23955         for (var index = 0; index < word.length; ++index) {
23956             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23957                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23958                     characterSetChecks[nCharSet].fResult = true;
23959                     break;
23960                 }
23961             }
23962         }
23963
23964         var nCharSets = 0;
23965         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23966             if (characterSetChecks[nCharSet].fResult) {
23967                 ++nCharSets;
23968             }
23969         }
23970
23971         if (nCharSets < nb) {
23972             return false;
23973         }
23974         return true;
23975     },
23976     // private
23977     ClientSideStrongPassword: function (pwd)
23978     {
23979         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23980     },
23981     // private
23982     ClientSideMediumPassword: function (pwd)
23983     {
23984         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23985     },
23986     // private
23987     ClientSideWeakPassword: function (pwd)
23988     {
23989         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23990     }
23991           
23992 })//<script type="text/javascript">
23993
23994 /*
23995  * Based  Ext JS Library 1.1.1
23996  * Copyright(c) 2006-2007, Ext JS, LLC.
23997  * LGPL
23998  *
23999  */
24000  
24001 /**
24002  * @class Roo.HtmlEditorCore
24003  * @extends Roo.Component
24004  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24005  *
24006  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24007  */
24008
24009 Roo.HtmlEditorCore = function(config){
24010     
24011     
24012     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24013     
24014     
24015     this.addEvents({
24016         /**
24017          * @event initialize
24018          * Fires when the editor is fully initialized (including the iframe)
24019          * @param {Roo.HtmlEditorCore} this
24020          */
24021         initialize: true,
24022         /**
24023          * @event activate
24024          * Fires when the editor is first receives the focus. Any insertion must wait
24025          * until after this event.
24026          * @param {Roo.HtmlEditorCore} this
24027          */
24028         activate: true,
24029          /**
24030          * @event beforesync
24031          * Fires before the textarea is updated with content from the editor iframe. Return false
24032          * to cancel the sync.
24033          * @param {Roo.HtmlEditorCore} this
24034          * @param {String} html
24035          */
24036         beforesync: true,
24037          /**
24038          * @event beforepush
24039          * Fires before the iframe editor is updated with content from the textarea. Return false
24040          * to cancel the push.
24041          * @param {Roo.HtmlEditorCore} this
24042          * @param {String} html
24043          */
24044         beforepush: true,
24045          /**
24046          * @event sync
24047          * Fires when the textarea is updated with content from the editor iframe.
24048          * @param {Roo.HtmlEditorCore} this
24049          * @param {String} html
24050          */
24051         sync: true,
24052          /**
24053          * @event push
24054          * Fires when the iframe editor is updated with content from the textarea.
24055          * @param {Roo.HtmlEditorCore} this
24056          * @param {String} html
24057          */
24058         push: true,
24059         
24060         /**
24061          * @event editorevent
24062          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24063          * @param {Roo.HtmlEditorCore} this
24064          */
24065         editorevent: true
24066         
24067     });
24068     
24069     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24070     
24071     // defaults : white / black...
24072     this.applyBlacklists();
24073     
24074     
24075     
24076 };
24077
24078
24079 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24080
24081
24082      /**
24083      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24084      */
24085     
24086     owner : false,
24087     
24088      /**
24089      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24090      *                        Roo.resizable.
24091      */
24092     resizable : false,
24093      /**
24094      * @cfg {Number} height (in pixels)
24095      */   
24096     height: 300,
24097    /**
24098      * @cfg {Number} width (in pixels)
24099      */   
24100     width: 500,
24101     
24102     /**
24103      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24104      * 
24105      */
24106     stylesheets: false,
24107     
24108     // id of frame..
24109     frameId: false,
24110     
24111     // private properties
24112     validationEvent : false,
24113     deferHeight: true,
24114     initialized : false,
24115     activated : false,
24116     sourceEditMode : false,
24117     onFocus : Roo.emptyFn,
24118     iframePad:3,
24119     hideMode:'offsets',
24120     
24121     clearUp: true,
24122     
24123     // blacklist + whitelisted elements..
24124     black: false,
24125     white: false,
24126      
24127     bodyCls : '',
24128
24129     /**
24130      * Protected method that will not generally be called directly. It
24131      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24132      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24133      */
24134     getDocMarkup : function(){
24135         // body styles..
24136         var st = '';
24137         
24138         // inherit styels from page...?? 
24139         if (this.stylesheets === false) {
24140             
24141             Roo.get(document.head).select('style').each(function(node) {
24142                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24143             });
24144             
24145             Roo.get(document.head).select('link').each(function(node) { 
24146                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24147             });
24148             
24149         } else if (!this.stylesheets.length) {
24150                 // simple..
24151                 st = '<style type="text/css">' +
24152                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24153                    '</style>';
24154         } else {
24155             for (var i in this.stylesheets) { 
24156                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24157             }
24158             
24159         }
24160         
24161         st +=  '<style type="text/css">' +
24162             'IMG { cursor: pointer } ' +
24163         '</style>';
24164
24165         var cls = 'roo-htmleditor-body';
24166         
24167         if(this.bodyCls.length){
24168             cls += ' ' + this.bodyCls;
24169         }
24170         
24171         return '<html><head>' + st  +
24172             //<style type="text/css">' +
24173             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24174             //'</style>' +
24175             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24176     },
24177
24178     // private
24179     onRender : function(ct, position)
24180     {
24181         var _t = this;
24182         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24183         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24184         
24185         
24186         this.el.dom.style.border = '0 none';
24187         this.el.dom.setAttribute('tabIndex', -1);
24188         this.el.addClass('x-hidden hide');
24189         
24190         
24191         
24192         if(Roo.isIE){ // fix IE 1px bogus margin
24193             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24194         }
24195        
24196         
24197         this.frameId = Roo.id();
24198         
24199          
24200         
24201         var iframe = this.owner.wrap.createChild({
24202             tag: 'iframe',
24203             cls: 'form-control', // bootstrap..
24204             id: this.frameId,
24205             name: this.frameId,
24206             frameBorder : 'no',
24207             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24208         }, this.el
24209         );
24210         
24211         
24212         this.iframe = iframe.dom;
24213
24214          this.assignDocWin();
24215         
24216         this.doc.designMode = 'on';
24217        
24218         this.doc.open();
24219         this.doc.write(this.getDocMarkup());
24220         this.doc.close();
24221
24222         
24223         var task = { // must defer to wait for browser to be ready
24224             run : function(){
24225                 //console.log("run task?" + this.doc.readyState);
24226                 this.assignDocWin();
24227                 if(this.doc.body || this.doc.readyState == 'complete'){
24228                     try {
24229                         this.doc.designMode="on";
24230                     } catch (e) {
24231                         return;
24232                     }
24233                     Roo.TaskMgr.stop(task);
24234                     this.initEditor.defer(10, this);
24235                 }
24236             },
24237             interval : 10,
24238             duration: 10000,
24239             scope: this
24240         };
24241         Roo.TaskMgr.start(task);
24242
24243     },
24244
24245     // private
24246     onResize : function(w, h)
24247     {
24248          Roo.log('resize: ' +w + ',' + h );
24249         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24250         if(!this.iframe){
24251             return;
24252         }
24253         if(typeof w == 'number'){
24254             
24255             this.iframe.style.width = w + 'px';
24256         }
24257         if(typeof h == 'number'){
24258             
24259             this.iframe.style.height = h + 'px';
24260             if(this.doc){
24261                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24262             }
24263         }
24264         
24265     },
24266
24267     /**
24268      * Toggles the editor between standard and source edit mode.
24269      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24270      */
24271     toggleSourceEdit : function(sourceEditMode){
24272         
24273         this.sourceEditMode = sourceEditMode === true;
24274         
24275         if(this.sourceEditMode){
24276  
24277             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24278             
24279         }else{
24280             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24281             //this.iframe.className = '';
24282             this.deferFocus();
24283         }
24284         //this.setSize(this.owner.wrap.getSize());
24285         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24286     },
24287
24288     
24289   
24290
24291     /**
24292      * Protected method that will not generally be called directly. If you need/want
24293      * custom HTML cleanup, this is the method you should override.
24294      * @param {String} html The HTML to be cleaned
24295      * return {String} The cleaned HTML
24296      */
24297     cleanHtml : function(html){
24298         html = String(html);
24299         if(html.length > 5){
24300             if(Roo.isSafari){ // strip safari nonsense
24301                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24302             }
24303         }
24304         if(html == '&nbsp;'){
24305             html = '';
24306         }
24307         return html;
24308     },
24309
24310     /**
24311      * HTML Editor -> Textarea
24312      * Protected method that will not generally be called directly. Syncs the contents
24313      * of the editor iframe with the textarea.
24314      */
24315     syncValue : function(){
24316         if(this.initialized){
24317             var bd = (this.doc.body || this.doc.documentElement);
24318             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24319             var html = bd.innerHTML;
24320             if(Roo.isSafari){
24321                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24322                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24323                 if(m && m[1]){
24324                     html = '<div style="'+m[0]+'">' + html + '</div>';
24325                 }
24326             }
24327             html = this.cleanHtml(html);
24328             // fix up the special chars.. normaly like back quotes in word...
24329             // however we do not want to do this with chinese..
24330             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24331                 
24332                 var cc = match.charCodeAt();
24333
24334                 // Get the character value, handling surrogate pairs
24335                 if (match.length == 2) {
24336                     // It's a surrogate pair, calculate the Unicode code point
24337                     var high = match.charCodeAt(0) - 0xD800;
24338                     var low  = match.charCodeAt(1) - 0xDC00;
24339                     cc = (high * 0x400) + low + 0x10000;
24340                 }  else if (
24341                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24342                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24343                     (cc >= 0xf900 && cc < 0xfb00 )
24344                 ) {
24345                         return match;
24346                 }  
24347          
24348                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24349                 return "&#" + cc + ";";
24350                 
24351                 
24352             });
24353             
24354             
24355              
24356             if(this.owner.fireEvent('beforesync', this, html) !== false){
24357                 this.el.dom.value = html;
24358                 this.owner.fireEvent('sync', this, html);
24359             }
24360         }
24361     },
24362
24363     /**
24364      * Protected method that will not generally be called directly. Pushes the value of the textarea
24365      * into the iframe editor.
24366      */
24367     pushValue : function(){
24368         if(this.initialized){
24369             var v = this.el.dom.value.trim();
24370             
24371 //            if(v.length < 1){
24372 //                v = '&#160;';
24373 //            }
24374             
24375             if(this.owner.fireEvent('beforepush', this, v) !== false){
24376                 var d = (this.doc.body || this.doc.documentElement);
24377                 d.innerHTML = v;
24378                 this.cleanUpPaste();
24379                 this.el.dom.value = d.innerHTML;
24380                 this.owner.fireEvent('push', this, v);
24381             }
24382         }
24383     },
24384
24385     // private
24386     deferFocus : function(){
24387         this.focus.defer(10, this);
24388     },
24389
24390     // doc'ed in Field
24391     focus : function(){
24392         if(this.win && !this.sourceEditMode){
24393             this.win.focus();
24394         }else{
24395             this.el.focus();
24396         }
24397     },
24398     
24399     assignDocWin: function()
24400     {
24401         var iframe = this.iframe;
24402         
24403          if(Roo.isIE){
24404             this.doc = iframe.contentWindow.document;
24405             this.win = iframe.contentWindow;
24406         } else {
24407 //            if (!Roo.get(this.frameId)) {
24408 //                return;
24409 //            }
24410 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24411 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24412             
24413             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24414                 return;
24415             }
24416             
24417             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24418             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24419         }
24420     },
24421     
24422     // private
24423     initEditor : function(){
24424         //console.log("INIT EDITOR");
24425         this.assignDocWin();
24426         
24427         
24428         
24429         this.doc.designMode="on";
24430         this.doc.open();
24431         this.doc.write(this.getDocMarkup());
24432         this.doc.close();
24433         
24434         var dbody = (this.doc.body || this.doc.documentElement);
24435         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24436         // this copies styles from the containing element into thsi one..
24437         // not sure why we need all of this..
24438         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24439         
24440         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24441         //ss['background-attachment'] = 'fixed'; // w3c
24442         dbody.bgProperties = 'fixed'; // ie
24443         //Roo.DomHelper.applyStyles(dbody, ss);
24444         Roo.EventManager.on(this.doc, {
24445             //'mousedown': this.onEditorEvent,
24446             'mouseup': this.onEditorEvent,
24447             'dblclick': this.onEditorEvent,
24448             'click': this.onEditorEvent,
24449             'keyup': this.onEditorEvent,
24450             buffer:100,
24451             scope: this
24452         });
24453         if(Roo.isGecko){
24454             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24455         }
24456         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24457             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24458         }
24459         this.initialized = true;
24460
24461         this.owner.fireEvent('initialize', this);
24462         this.pushValue();
24463     },
24464
24465     // private
24466     onDestroy : function(){
24467         
24468         
24469         
24470         if(this.rendered){
24471             
24472             //for (var i =0; i < this.toolbars.length;i++) {
24473             //    // fixme - ask toolbars for heights?
24474             //    this.toolbars[i].onDestroy();
24475            // }
24476             
24477             //this.wrap.dom.innerHTML = '';
24478             //this.wrap.remove();
24479         }
24480     },
24481
24482     // private
24483     onFirstFocus : function(){
24484         
24485         this.assignDocWin();
24486         
24487         
24488         this.activated = true;
24489          
24490     
24491         if(Roo.isGecko){ // prevent silly gecko errors
24492             this.win.focus();
24493             var s = this.win.getSelection();
24494             if(!s.focusNode || s.focusNode.nodeType != 3){
24495                 var r = s.getRangeAt(0);
24496                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24497                 r.collapse(true);
24498                 this.deferFocus();
24499             }
24500             try{
24501                 this.execCmd('useCSS', true);
24502                 this.execCmd('styleWithCSS', false);
24503             }catch(e){}
24504         }
24505         this.owner.fireEvent('activate', this);
24506     },
24507
24508     // private
24509     adjustFont: function(btn){
24510         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24511         //if(Roo.isSafari){ // safari
24512         //    adjust *= 2;
24513        // }
24514         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24515         if(Roo.isSafari){ // safari
24516             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24517             v =  (v < 10) ? 10 : v;
24518             v =  (v > 48) ? 48 : v;
24519             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24520             
24521         }
24522         
24523         
24524         v = Math.max(1, v+adjust);
24525         
24526         this.execCmd('FontSize', v  );
24527     },
24528
24529     onEditorEvent : function(e)
24530     {
24531         this.owner.fireEvent('editorevent', this, e);
24532       //  this.updateToolbar();
24533         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24534     },
24535
24536     insertTag : function(tg)
24537     {
24538         // could be a bit smarter... -> wrap the current selected tRoo..
24539         if (tg.toLowerCase() == 'span' ||
24540             tg.toLowerCase() == 'code' ||
24541             tg.toLowerCase() == 'sup' ||
24542             tg.toLowerCase() == 'sub' 
24543             ) {
24544             
24545             range = this.createRange(this.getSelection());
24546             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24547             wrappingNode.appendChild(range.extractContents());
24548             range.insertNode(wrappingNode);
24549
24550             return;
24551             
24552             
24553             
24554         }
24555         this.execCmd("formatblock",   tg);
24556         
24557     },
24558     
24559     insertText : function(txt)
24560     {
24561         
24562         
24563         var range = this.createRange();
24564         range.deleteContents();
24565                //alert(Sender.getAttribute('label'));
24566                
24567         range.insertNode(this.doc.createTextNode(txt));
24568     } ,
24569     
24570      
24571
24572     /**
24573      * Executes a Midas editor command on the editor document and performs necessary focus and
24574      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24575      * @param {String} cmd The Midas command
24576      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24577      */
24578     relayCmd : function(cmd, value){
24579         this.win.focus();
24580         this.execCmd(cmd, value);
24581         this.owner.fireEvent('editorevent', this);
24582         //this.updateToolbar();
24583         this.owner.deferFocus();
24584     },
24585
24586     /**
24587      * Executes a Midas editor command directly on the editor document.
24588      * For visual commands, you should use {@link #relayCmd} instead.
24589      * <b>This should only be called after the editor is initialized.</b>
24590      * @param {String} cmd The Midas command
24591      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24592      */
24593     execCmd : function(cmd, value){
24594         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24595         this.syncValue();
24596     },
24597  
24598  
24599    
24600     /**
24601      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24602      * to insert tRoo.
24603      * @param {String} text | dom node.. 
24604      */
24605     insertAtCursor : function(text)
24606     {
24607         
24608         if(!this.activated){
24609             return;
24610         }
24611         /*
24612         if(Roo.isIE){
24613             this.win.focus();
24614             var r = this.doc.selection.createRange();
24615             if(r){
24616                 r.collapse(true);
24617                 r.pasteHTML(text);
24618                 this.syncValue();
24619                 this.deferFocus();
24620             
24621             }
24622             return;
24623         }
24624         */
24625         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24626             this.win.focus();
24627             
24628             
24629             // from jquery ui (MIT licenced)
24630             var range, node;
24631             var win = this.win;
24632             
24633             if (win.getSelection && win.getSelection().getRangeAt) {
24634                 range = win.getSelection().getRangeAt(0);
24635                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24636                 range.insertNode(node);
24637             } else if (win.document.selection && win.document.selection.createRange) {
24638                 // no firefox support
24639                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24640                 win.document.selection.createRange().pasteHTML(txt);
24641             } else {
24642                 // no firefox support
24643                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24644                 this.execCmd('InsertHTML', txt);
24645             } 
24646             
24647             this.syncValue();
24648             
24649             this.deferFocus();
24650         }
24651     },
24652  // private
24653     mozKeyPress : function(e){
24654         if(e.ctrlKey){
24655             var c = e.getCharCode(), cmd;
24656           
24657             if(c > 0){
24658                 c = String.fromCharCode(c).toLowerCase();
24659                 switch(c){
24660                     case 'b':
24661                         cmd = 'bold';
24662                         break;
24663                     case 'i':
24664                         cmd = 'italic';
24665                         break;
24666                     
24667                     case 'u':
24668                         cmd = 'underline';
24669                         break;
24670                     
24671                     case 'v':
24672                         this.cleanUpPaste.defer(100, this);
24673                         return;
24674                         
24675                 }
24676                 if(cmd){
24677                     this.win.focus();
24678                     this.execCmd(cmd);
24679                     this.deferFocus();
24680                     e.preventDefault();
24681                 }
24682                 
24683             }
24684         }
24685     },
24686
24687     // private
24688     fixKeys : function(){ // load time branching for fastest keydown performance
24689         if(Roo.isIE){
24690             return function(e){
24691                 var k = e.getKey(), r;
24692                 if(k == e.TAB){
24693                     e.stopEvent();
24694                     r = this.doc.selection.createRange();
24695                     if(r){
24696                         r.collapse(true);
24697                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24698                         this.deferFocus();
24699                     }
24700                     return;
24701                 }
24702                 
24703                 if(k == e.ENTER){
24704                     r = this.doc.selection.createRange();
24705                     if(r){
24706                         var target = r.parentElement();
24707                         if(!target || target.tagName.toLowerCase() != 'li'){
24708                             e.stopEvent();
24709                             r.pasteHTML('<br />');
24710                             r.collapse(false);
24711                             r.select();
24712                         }
24713                     }
24714                 }
24715                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24716                     this.cleanUpPaste.defer(100, this);
24717                     return;
24718                 }
24719                 
24720                 
24721             };
24722         }else if(Roo.isOpera){
24723             return function(e){
24724                 var k = e.getKey();
24725                 if(k == e.TAB){
24726                     e.stopEvent();
24727                     this.win.focus();
24728                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24729                     this.deferFocus();
24730                 }
24731                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24732                     this.cleanUpPaste.defer(100, this);
24733                     return;
24734                 }
24735                 
24736             };
24737         }else if(Roo.isSafari){
24738             return function(e){
24739                 var k = e.getKey();
24740                 
24741                 if(k == e.TAB){
24742                     e.stopEvent();
24743                     this.execCmd('InsertText','\t');
24744                     this.deferFocus();
24745                     return;
24746                 }
24747                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24748                     this.cleanUpPaste.defer(100, this);
24749                     return;
24750                 }
24751                 
24752              };
24753         }
24754     }(),
24755     
24756     getAllAncestors: function()
24757     {
24758         var p = this.getSelectedNode();
24759         var a = [];
24760         if (!p) {
24761             a.push(p); // push blank onto stack..
24762             p = this.getParentElement();
24763         }
24764         
24765         
24766         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24767             a.push(p);
24768             p = p.parentNode;
24769         }
24770         a.push(this.doc.body);
24771         return a;
24772     },
24773     lastSel : false,
24774     lastSelNode : false,
24775     
24776     
24777     getSelection : function() 
24778     {
24779         this.assignDocWin();
24780         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24781     },
24782     
24783     getSelectedNode: function() 
24784     {
24785         // this may only work on Gecko!!!
24786         
24787         // should we cache this!!!!
24788         
24789         
24790         
24791          
24792         var range = this.createRange(this.getSelection()).cloneRange();
24793         
24794         if (Roo.isIE) {
24795             var parent = range.parentElement();
24796             while (true) {
24797                 var testRange = range.duplicate();
24798                 testRange.moveToElementText(parent);
24799                 if (testRange.inRange(range)) {
24800                     break;
24801                 }
24802                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24803                     break;
24804                 }
24805                 parent = parent.parentElement;
24806             }
24807             return parent;
24808         }
24809         
24810         // is ancestor a text element.
24811         var ac =  range.commonAncestorContainer;
24812         if (ac.nodeType == 3) {
24813             ac = ac.parentNode;
24814         }
24815         
24816         var ar = ac.childNodes;
24817          
24818         var nodes = [];
24819         var other_nodes = [];
24820         var has_other_nodes = false;
24821         for (var i=0;i<ar.length;i++) {
24822             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24823                 continue;
24824             }
24825             // fullly contained node.
24826             
24827             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24828                 nodes.push(ar[i]);
24829                 continue;
24830             }
24831             
24832             // probably selected..
24833             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24834                 other_nodes.push(ar[i]);
24835                 continue;
24836             }
24837             // outer..
24838             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24839                 continue;
24840             }
24841             
24842             
24843             has_other_nodes = true;
24844         }
24845         if (!nodes.length && other_nodes.length) {
24846             nodes= other_nodes;
24847         }
24848         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24849             return false;
24850         }
24851         
24852         return nodes[0];
24853     },
24854     createRange: function(sel)
24855     {
24856         // this has strange effects when using with 
24857         // top toolbar - not sure if it's a great idea.
24858         //this.editor.contentWindow.focus();
24859         if (typeof sel != "undefined") {
24860             try {
24861                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24862             } catch(e) {
24863                 return this.doc.createRange();
24864             }
24865         } else {
24866             return this.doc.createRange();
24867         }
24868     },
24869     getParentElement: function()
24870     {
24871         
24872         this.assignDocWin();
24873         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24874         
24875         var range = this.createRange(sel);
24876          
24877         try {
24878             var p = range.commonAncestorContainer;
24879             while (p.nodeType == 3) { // text node
24880                 p = p.parentNode;
24881             }
24882             return p;
24883         } catch (e) {
24884             return null;
24885         }
24886     
24887     },
24888     /***
24889      *
24890      * Range intersection.. the hard stuff...
24891      *  '-1' = before
24892      *  '0' = hits..
24893      *  '1' = after.
24894      *         [ -- selected range --- ]
24895      *   [fail]                        [fail]
24896      *
24897      *    basically..
24898      *      if end is before start or  hits it. fail.
24899      *      if start is after end or hits it fail.
24900      *
24901      *   if either hits (but other is outside. - then it's not 
24902      *   
24903      *    
24904      **/
24905     
24906     
24907     // @see http://www.thismuchiknow.co.uk/?p=64.
24908     rangeIntersectsNode : function(range, node)
24909     {
24910         var nodeRange = node.ownerDocument.createRange();
24911         try {
24912             nodeRange.selectNode(node);
24913         } catch (e) {
24914             nodeRange.selectNodeContents(node);
24915         }
24916     
24917         var rangeStartRange = range.cloneRange();
24918         rangeStartRange.collapse(true);
24919     
24920         var rangeEndRange = range.cloneRange();
24921         rangeEndRange.collapse(false);
24922     
24923         var nodeStartRange = nodeRange.cloneRange();
24924         nodeStartRange.collapse(true);
24925     
24926         var nodeEndRange = nodeRange.cloneRange();
24927         nodeEndRange.collapse(false);
24928     
24929         return rangeStartRange.compareBoundaryPoints(
24930                  Range.START_TO_START, nodeEndRange) == -1 &&
24931                rangeEndRange.compareBoundaryPoints(
24932                  Range.START_TO_START, nodeStartRange) == 1;
24933         
24934          
24935     },
24936     rangeCompareNode : function(range, node)
24937     {
24938         var nodeRange = node.ownerDocument.createRange();
24939         try {
24940             nodeRange.selectNode(node);
24941         } catch (e) {
24942             nodeRange.selectNodeContents(node);
24943         }
24944         
24945         
24946         range.collapse(true);
24947     
24948         nodeRange.collapse(true);
24949      
24950         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24951         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24952          
24953         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24954         
24955         var nodeIsBefore   =  ss == 1;
24956         var nodeIsAfter    = ee == -1;
24957         
24958         if (nodeIsBefore && nodeIsAfter) {
24959             return 0; // outer
24960         }
24961         if (!nodeIsBefore && nodeIsAfter) {
24962             return 1; //right trailed.
24963         }
24964         
24965         if (nodeIsBefore && !nodeIsAfter) {
24966             return 2;  // left trailed.
24967         }
24968         // fully contined.
24969         return 3;
24970     },
24971
24972     // private? - in a new class?
24973     cleanUpPaste :  function()
24974     {
24975         // cleans up the whole document..
24976         Roo.log('cleanuppaste');
24977         
24978         this.cleanUpChildren(this.doc.body);
24979         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24980         if (clean != this.doc.body.innerHTML) {
24981             this.doc.body.innerHTML = clean;
24982         }
24983         
24984     },
24985     
24986     cleanWordChars : function(input) {// change the chars to hex code
24987         var he = Roo.HtmlEditorCore;
24988         
24989         var output = input;
24990         Roo.each(he.swapCodes, function(sw) { 
24991             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24992             
24993             output = output.replace(swapper, sw[1]);
24994         });
24995         
24996         return output;
24997     },
24998     
24999     
25000     cleanUpChildren : function (n)
25001     {
25002         if (!n.childNodes.length) {
25003             return;
25004         }
25005         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25006            this.cleanUpChild(n.childNodes[i]);
25007         }
25008     },
25009     
25010     
25011         
25012     
25013     cleanUpChild : function (node)
25014     {
25015         var ed = this;
25016         //console.log(node);
25017         if (node.nodeName == "#text") {
25018             // clean up silly Windows -- stuff?
25019             return; 
25020         }
25021         if (node.nodeName == "#comment") {
25022             node.parentNode.removeChild(node);
25023             // clean up silly Windows -- stuff?
25024             return; 
25025         }
25026         var lcname = node.tagName.toLowerCase();
25027         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25028         // whitelist of tags..
25029         
25030         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25031             // remove node.
25032             node.parentNode.removeChild(node);
25033             return;
25034             
25035         }
25036         
25037         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25038         
25039         // spans with no attributes - just remove them..
25040         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25041             remove_keep_children = true;
25042         }
25043         
25044         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25045         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25046         
25047         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25048         //    remove_keep_children = true;
25049         //}
25050         
25051         if (remove_keep_children) {
25052             this.cleanUpChildren(node);
25053             // inserts everything just before this node...
25054             while (node.childNodes.length) {
25055                 var cn = node.childNodes[0];
25056                 node.removeChild(cn);
25057                 node.parentNode.insertBefore(cn, node);
25058             }
25059             node.parentNode.removeChild(node);
25060             return;
25061         }
25062         
25063         if (!node.attributes || !node.attributes.length) {
25064             
25065           
25066             
25067             
25068             this.cleanUpChildren(node);
25069             return;
25070         }
25071         
25072         function cleanAttr(n,v)
25073         {
25074             
25075             if (v.match(/^\./) || v.match(/^\//)) {
25076                 return;
25077             }
25078             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25079                 return;
25080             }
25081             if (v.match(/^#/)) {
25082                 return;
25083             }
25084             if (v.match(/^\{/)) { // allow template editing.
25085                 return;
25086             }
25087 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25088             node.removeAttribute(n);
25089             
25090         }
25091         
25092         var cwhite = this.cwhite;
25093         var cblack = this.cblack;
25094             
25095         function cleanStyle(n,v)
25096         {
25097             if (v.match(/expression/)) { //XSS?? should we even bother..
25098                 node.removeAttribute(n);
25099                 return;
25100             }
25101             
25102             var parts = v.split(/;/);
25103             var clean = [];
25104             
25105             Roo.each(parts, function(p) {
25106                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25107                 if (!p.length) {
25108                     return true;
25109                 }
25110                 var l = p.split(':').shift().replace(/\s+/g,'');
25111                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25112                 
25113                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25114 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25115                     //node.removeAttribute(n);
25116                     return true;
25117                 }
25118                 //Roo.log()
25119                 // only allow 'c whitelisted system attributes'
25120                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25121 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25122                     //node.removeAttribute(n);
25123                     return true;
25124                 }
25125                 
25126                 
25127                  
25128                 
25129                 clean.push(p);
25130                 return true;
25131             });
25132             if (clean.length) { 
25133                 node.setAttribute(n, clean.join(';'));
25134             } else {
25135                 node.removeAttribute(n);
25136             }
25137             
25138         }
25139         
25140         
25141         for (var i = node.attributes.length-1; i > -1 ; i--) {
25142             var a = node.attributes[i];
25143             //console.log(a);
25144             
25145             if (a.name.toLowerCase().substr(0,2)=='on')  {
25146                 node.removeAttribute(a.name);
25147                 continue;
25148             }
25149             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25150                 node.removeAttribute(a.name);
25151                 continue;
25152             }
25153             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25154                 cleanAttr(a.name,a.value); // fixme..
25155                 continue;
25156             }
25157             if (a.name == 'style') {
25158                 cleanStyle(a.name,a.value);
25159                 continue;
25160             }
25161             /// clean up MS crap..
25162             // tecnically this should be a list of valid class'es..
25163             
25164             
25165             if (a.name == 'class') {
25166                 if (a.value.match(/^Mso/)) {
25167                     node.removeAttribute('class');
25168                 }
25169                 
25170                 if (a.value.match(/^body$/)) {
25171                     node.removeAttribute('class');
25172                 }
25173                 continue;
25174             }
25175             
25176             // style cleanup!?
25177             // class cleanup?
25178             
25179         }
25180         
25181         
25182         this.cleanUpChildren(node);
25183         
25184         
25185     },
25186     
25187     /**
25188      * Clean up MS wordisms...
25189      */
25190     cleanWord : function(node)
25191     {
25192         if (!node) {
25193             this.cleanWord(this.doc.body);
25194             return;
25195         }
25196         
25197         if(
25198                 node.nodeName == 'SPAN' &&
25199                 !node.hasAttributes() &&
25200                 node.childNodes.length == 1 &&
25201                 node.firstChild.nodeName == "#text"  
25202         ) {
25203             var textNode = node.firstChild;
25204             node.removeChild(textNode);
25205             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25206                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25207             }
25208             node.parentNode.insertBefore(textNode, node);
25209             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25210                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25211             }
25212             node.parentNode.removeChild(node);
25213         }
25214         
25215         if (node.nodeName == "#text") {
25216             // clean up silly Windows -- stuff?
25217             return; 
25218         }
25219         if (node.nodeName == "#comment") {
25220             node.parentNode.removeChild(node);
25221             // clean up silly Windows -- stuff?
25222             return; 
25223         }
25224         
25225         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25226             node.parentNode.removeChild(node);
25227             return;
25228         }
25229         //Roo.log(node.tagName);
25230         // remove - but keep children..
25231         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25232             //Roo.log('-- removed');
25233             while (node.childNodes.length) {
25234                 var cn = node.childNodes[0];
25235                 node.removeChild(cn);
25236                 node.parentNode.insertBefore(cn, node);
25237                 // move node to parent - and clean it..
25238                 this.cleanWord(cn);
25239             }
25240             node.parentNode.removeChild(node);
25241             /// no need to iterate chidlren = it's got none..
25242             //this.iterateChildren(node, this.cleanWord);
25243             return;
25244         }
25245         // clean styles
25246         if (node.className.length) {
25247             
25248             var cn = node.className.split(/\W+/);
25249             var cna = [];
25250             Roo.each(cn, function(cls) {
25251                 if (cls.match(/Mso[a-zA-Z]+/)) {
25252                     return;
25253                 }
25254                 cna.push(cls);
25255             });
25256             node.className = cna.length ? cna.join(' ') : '';
25257             if (!cna.length) {
25258                 node.removeAttribute("class");
25259             }
25260         }
25261         
25262         if (node.hasAttribute("lang")) {
25263             node.removeAttribute("lang");
25264         }
25265         
25266         if (node.hasAttribute("style")) {
25267             
25268             var styles = node.getAttribute("style").split(";");
25269             var nstyle = [];
25270             Roo.each(styles, function(s) {
25271                 if (!s.match(/:/)) {
25272                     return;
25273                 }
25274                 var kv = s.split(":");
25275                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25276                     return;
25277                 }
25278                 // what ever is left... we allow.
25279                 nstyle.push(s);
25280             });
25281             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25282             if (!nstyle.length) {
25283                 node.removeAttribute('style');
25284             }
25285         }
25286         this.iterateChildren(node, this.cleanWord);
25287         
25288         
25289         
25290     },
25291     /**
25292      * iterateChildren of a Node, calling fn each time, using this as the scole..
25293      * @param {DomNode} node node to iterate children of.
25294      * @param {Function} fn method of this class to call on each item.
25295      */
25296     iterateChildren : function(node, fn)
25297     {
25298         if (!node.childNodes.length) {
25299                 return;
25300         }
25301         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25302            fn.call(this, node.childNodes[i])
25303         }
25304     },
25305     
25306     
25307     /**
25308      * cleanTableWidths.
25309      *
25310      * Quite often pasting from word etc.. results in tables with column and widths.
25311      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25312      *
25313      */
25314     cleanTableWidths : function(node)
25315     {
25316          
25317          
25318         if (!node) {
25319             this.cleanTableWidths(this.doc.body);
25320             return;
25321         }
25322         
25323         // ignore list...
25324         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25325             return; 
25326         }
25327         Roo.log(node.tagName);
25328         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25329             this.iterateChildren(node, this.cleanTableWidths);
25330             return;
25331         }
25332         if (node.hasAttribute('width')) {
25333             node.removeAttribute('width');
25334         }
25335         
25336          
25337         if (node.hasAttribute("style")) {
25338             // pretty basic...
25339             
25340             var styles = node.getAttribute("style").split(";");
25341             var nstyle = [];
25342             Roo.each(styles, function(s) {
25343                 if (!s.match(/:/)) {
25344                     return;
25345                 }
25346                 var kv = s.split(":");
25347                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25348                     return;
25349                 }
25350                 // what ever is left... we allow.
25351                 nstyle.push(s);
25352             });
25353             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25354             if (!nstyle.length) {
25355                 node.removeAttribute('style');
25356             }
25357         }
25358         
25359         this.iterateChildren(node, this.cleanTableWidths);
25360         
25361         
25362     },
25363     
25364     
25365     
25366     
25367     domToHTML : function(currentElement, depth, nopadtext) {
25368         
25369         depth = depth || 0;
25370         nopadtext = nopadtext || false;
25371     
25372         if (!currentElement) {
25373             return this.domToHTML(this.doc.body);
25374         }
25375         
25376         //Roo.log(currentElement);
25377         var j;
25378         var allText = false;
25379         var nodeName = currentElement.nodeName;
25380         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25381         
25382         if  (nodeName == '#text') {
25383             
25384             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25385         }
25386         
25387         
25388         var ret = '';
25389         if (nodeName != 'BODY') {
25390              
25391             var i = 0;
25392             // Prints the node tagName, such as <A>, <IMG>, etc
25393             if (tagName) {
25394                 var attr = [];
25395                 for(i = 0; i < currentElement.attributes.length;i++) {
25396                     // quoting?
25397                     var aname = currentElement.attributes.item(i).name;
25398                     if (!currentElement.attributes.item(i).value.length) {
25399                         continue;
25400                     }
25401                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25402                 }
25403                 
25404                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25405             } 
25406             else {
25407                 
25408                 // eack
25409             }
25410         } else {
25411             tagName = false;
25412         }
25413         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25414             return ret;
25415         }
25416         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25417             nopadtext = true;
25418         }
25419         
25420         
25421         // Traverse the tree
25422         i = 0;
25423         var currentElementChild = currentElement.childNodes.item(i);
25424         var allText = true;
25425         var innerHTML  = '';
25426         lastnode = '';
25427         while (currentElementChild) {
25428             // Formatting code (indent the tree so it looks nice on the screen)
25429             var nopad = nopadtext;
25430             if (lastnode == 'SPAN') {
25431                 nopad  = true;
25432             }
25433             // text
25434             if  (currentElementChild.nodeName == '#text') {
25435                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25436                 toadd = nopadtext ? toadd : toadd.trim();
25437                 if (!nopad && toadd.length > 80) {
25438                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25439                 }
25440                 innerHTML  += toadd;
25441                 
25442                 i++;
25443                 currentElementChild = currentElement.childNodes.item(i);
25444                 lastNode = '';
25445                 continue;
25446             }
25447             allText = false;
25448             
25449             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25450                 
25451             // Recursively traverse the tree structure of the child node
25452             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25453             lastnode = currentElementChild.nodeName;
25454             i++;
25455             currentElementChild=currentElement.childNodes.item(i);
25456         }
25457         
25458         ret += innerHTML;
25459         
25460         if (!allText) {
25461                 // The remaining code is mostly for formatting the tree
25462             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25463         }
25464         
25465         
25466         if (tagName) {
25467             ret+= "</"+tagName+">";
25468         }
25469         return ret;
25470         
25471     },
25472         
25473     applyBlacklists : function()
25474     {
25475         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25476         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25477         
25478         this.white = [];
25479         this.black = [];
25480         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25481             if (b.indexOf(tag) > -1) {
25482                 return;
25483             }
25484             this.white.push(tag);
25485             
25486         }, this);
25487         
25488         Roo.each(w, function(tag) {
25489             if (b.indexOf(tag) > -1) {
25490                 return;
25491             }
25492             if (this.white.indexOf(tag) > -1) {
25493                 return;
25494             }
25495             this.white.push(tag);
25496             
25497         }, this);
25498         
25499         
25500         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25501             if (w.indexOf(tag) > -1) {
25502                 return;
25503             }
25504             this.black.push(tag);
25505             
25506         }, this);
25507         
25508         Roo.each(b, function(tag) {
25509             if (w.indexOf(tag) > -1) {
25510                 return;
25511             }
25512             if (this.black.indexOf(tag) > -1) {
25513                 return;
25514             }
25515             this.black.push(tag);
25516             
25517         }, this);
25518         
25519         
25520         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25521         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25522         
25523         this.cwhite = [];
25524         this.cblack = [];
25525         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25526             if (b.indexOf(tag) > -1) {
25527                 return;
25528             }
25529             this.cwhite.push(tag);
25530             
25531         }, this);
25532         
25533         Roo.each(w, function(tag) {
25534             if (b.indexOf(tag) > -1) {
25535                 return;
25536             }
25537             if (this.cwhite.indexOf(tag) > -1) {
25538                 return;
25539             }
25540             this.cwhite.push(tag);
25541             
25542         }, this);
25543         
25544         
25545         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25546             if (w.indexOf(tag) > -1) {
25547                 return;
25548             }
25549             this.cblack.push(tag);
25550             
25551         }, this);
25552         
25553         Roo.each(b, function(tag) {
25554             if (w.indexOf(tag) > -1) {
25555                 return;
25556             }
25557             if (this.cblack.indexOf(tag) > -1) {
25558                 return;
25559             }
25560             this.cblack.push(tag);
25561             
25562         }, this);
25563     },
25564     
25565     setStylesheets : function(stylesheets)
25566     {
25567         if(typeof(stylesheets) == 'string'){
25568             Roo.get(this.iframe.contentDocument.head).createChild({
25569                 tag : 'link',
25570                 rel : 'stylesheet',
25571                 type : 'text/css',
25572                 href : stylesheets
25573             });
25574             
25575             return;
25576         }
25577         var _this = this;
25578      
25579         Roo.each(stylesheets, function(s) {
25580             if(!s.length){
25581                 return;
25582             }
25583             
25584             Roo.get(_this.iframe.contentDocument.head).createChild({
25585                 tag : 'link',
25586                 rel : 'stylesheet',
25587                 type : 'text/css',
25588                 href : s
25589             });
25590         });
25591
25592         
25593     },
25594     
25595     removeStylesheets : function()
25596     {
25597         var _this = this;
25598         
25599         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25600             s.remove();
25601         });
25602     },
25603     
25604     setStyle : function(style)
25605     {
25606         Roo.get(this.iframe.contentDocument.head).createChild({
25607             tag : 'style',
25608             type : 'text/css',
25609             html : style
25610         });
25611
25612         return;
25613     }
25614     
25615     // hide stuff that is not compatible
25616     /**
25617      * @event blur
25618      * @hide
25619      */
25620     /**
25621      * @event change
25622      * @hide
25623      */
25624     /**
25625      * @event focus
25626      * @hide
25627      */
25628     /**
25629      * @event specialkey
25630      * @hide
25631      */
25632     /**
25633      * @cfg {String} fieldClass @hide
25634      */
25635     /**
25636      * @cfg {String} focusClass @hide
25637      */
25638     /**
25639      * @cfg {String} autoCreate @hide
25640      */
25641     /**
25642      * @cfg {String} inputType @hide
25643      */
25644     /**
25645      * @cfg {String} invalidClass @hide
25646      */
25647     /**
25648      * @cfg {String} invalidText @hide
25649      */
25650     /**
25651      * @cfg {String} msgFx @hide
25652      */
25653     /**
25654      * @cfg {String} validateOnBlur @hide
25655      */
25656 });
25657
25658 Roo.HtmlEditorCore.white = [
25659         'area', 'br', 'img', 'input', 'hr', 'wbr',
25660         
25661        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25662        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25663        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25664        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25665        'table',   'ul',         'xmp', 
25666        
25667        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25668       'thead',   'tr', 
25669      
25670       'dir', 'menu', 'ol', 'ul', 'dl',
25671        
25672       'embed',  'object'
25673 ];
25674
25675
25676 Roo.HtmlEditorCore.black = [
25677     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25678         'applet', // 
25679         'base',   'basefont', 'bgsound', 'blink',  'body', 
25680         'frame',  'frameset', 'head',    'html',   'ilayer', 
25681         'iframe', 'layer',  'link',     'meta',    'object',   
25682         'script', 'style' ,'title',  'xml' // clean later..
25683 ];
25684 Roo.HtmlEditorCore.clean = [
25685     'script', 'style', 'title', 'xml'
25686 ];
25687 Roo.HtmlEditorCore.remove = [
25688     'font'
25689 ];
25690 // attributes..
25691
25692 Roo.HtmlEditorCore.ablack = [
25693     'on'
25694 ];
25695     
25696 Roo.HtmlEditorCore.aclean = [ 
25697     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25698 ];
25699
25700 // protocols..
25701 Roo.HtmlEditorCore.pwhite= [
25702         'http',  'https',  'mailto'
25703 ];
25704
25705 // white listed style attributes.
25706 Roo.HtmlEditorCore.cwhite= [
25707       //  'text-align', /// default is to allow most things..
25708       
25709          
25710 //        'font-size'//??
25711 ];
25712
25713 // black listed style attributes.
25714 Roo.HtmlEditorCore.cblack= [
25715       //  'font-size' -- this can be set by the project 
25716 ];
25717
25718
25719 Roo.HtmlEditorCore.swapCodes   =[ 
25720     [    8211, "--" ], 
25721     [    8212, "--" ], 
25722     [    8216,  "'" ],  
25723     [    8217, "'" ],  
25724     [    8220, '"' ],  
25725     [    8221, '"' ],  
25726     [    8226, "*" ],  
25727     [    8230, "..." ]
25728 ]; 
25729
25730     /*
25731  * - LGPL
25732  *
25733  * HtmlEditor
25734  * 
25735  */
25736
25737 /**
25738  * @class Roo.bootstrap.HtmlEditor
25739  * @extends Roo.bootstrap.TextArea
25740  * Bootstrap HtmlEditor class
25741
25742  * @constructor
25743  * Create a new HtmlEditor
25744  * @param {Object} config The config object
25745  */
25746
25747 Roo.bootstrap.HtmlEditor = function(config){
25748     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25749     if (!this.toolbars) {
25750         this.toolbars = [];
25751     }
25752     
25753     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25754     this.addEvents({
25755             /**
25756              * @event initialize
25757              * Fires when the editor is fully initialized (including the iframe)
25758              * @param {HtmlEditor} this
25759              */
25760             initialize: true,
25761             /**
25762              * @event activate
25763              * Fires when the editor is first receives the focus. Any insertion must wait
25764              * until after this event.
25765              * @param {HtmlEditor} this
25766              */
25767             activate: true,
25768              /**
25769              * @event beforesync
25770              * Fires before the textarea is updated with content from the editor iframe. Return false
25771              * to cancel the sync.
25772              * @param {HtmlEditor} this
25773              * @param {String} html
25774              */
25775             beforesync: true,
25776              /**
25777              * @event beforepush
25778              * Fires before the iframe editor is updated with content from the textarea. Return false
25779              * to cancel the push.
25780              * @param {HtmlEditor} this
25781              * @param {String} html
25782              */
25783             beforepush: true,
25784              /**
25785              * @event sync
25786              * Fires when the textarea is updated with content from the editor iframe.
25787              * @param {HtmlEditor} this
25788              * @param {String} html
25789              */
25790             sync: true,
25791              /**
25792              * @event push
25793              * Fires when the iframe editor is updated with content from the textarea.
25794              * @param {HtmlEditor} this
25795              * @param {String} html
25796              */
25797             push: true,
25798              /**
25799              * @event editmodechange
25800              * Fires when the editor switches edit modes
25801              * @param {HtmlEditor} this
25802              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25803              */
25804             editmodechange: true,
25805             /**
25806              * @event editorevent
25807              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25808              * @param {HtmlEditor} this
25809              */
25810             editorevent: true,
25811             /**
25812              * @event firstfocus
25813              * Fires when on first focus - needed by toolbars..
25814              * @param {HtmlEditor} this
25815              */
25816             firstfocus: true,
25817             /**
25818              * @event autosave
25819              * Auto save the htmlEditor value as a file into Events
25820              * @param {HtmlEditor} this
25821              */
25822             autosave: true,
25823             /**
25824              * @event savedpreview
25825              * preview the saved version of htmlEditor
25826              * @param {HtmlEditor} this
25827              */
25828             savedpreview: true
25829         });
25830 };
25831
25832
25833 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25834     
25835     
25836       /**
25837      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25838      */
25839     toolbars : false,
25840     
25841      /**
25842     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25843     */
25844     btns : [],
25845    
25846      /**
25847      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25848      *                        Roo.resizable.
25849      */
25850     resizable : false,
25851      /**
25852      * @cfg {Number} height (in pixels)
25853      */   
25854     height: 300,
25855    /**
25856      * @cfg {Number} width (in pixels)
25857      */   
25858     width: false,
25859     
25860     /**
25861      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25862      * 
25863      */
25864     stylesheets: false,
25865     
25866     // id of frame..
25867     frameId: false,
25868     
25869     // private properties
25870     validationEvent : false,
25871     deferHeight: true,
25872     initialized : false,
25873     activated : false,
25874     
25875     onFocus : Roo.emptyFn,
25876     iframePad:3,
25877     hideMode:'offsets',
25878     
25879     tbContainer : false,
25880     
25881     bodyCls : '',
25882     
25883     toolbarContainer :function() {
25884         return this.wrap.select('.x-html-editor-tb',true).first();
25885     },
25886
25887     /**
25888      * Protected method that will not generally be called directly. It
25889      * is called when the editor creates its toolbar. Override this method if you need to
25890      * add custom toolbar buttons.
25891      * @param {HtmlEditor} editor
25892      */
25893     createToolbar : function(){
25894         Roo.log('renewing');
25895         Roo.log("create toolbars");
25896         
25897         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25898         this.toolbars[0].render(this.toolbarContainer());
25899         
25900         return;
25901         
25902 //        if (!editor.toolbars || !editor.toolbars.length) {
25903 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25904 //        }
25905 //        
25906 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25907 //            editor.toolbars[i] = Roo.factory(
25908 //                    typeof(editor.toolbars[i]) == 'string' ?
25909 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25910 //                Roo.bootstrap.HtmlEditor);
25911 //            editor.toolbars[i].init(editor);
25912 //        }
25913     },
25914
25915      
25916     // private
25917     onRender : function(ct, position)
25918     {
25919        // Roo.log("Call onRender: " + this.xtype);
25920         var _t = this;
25921         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25922       
25923         this.wrap = this.inputEl().wrap({
25924             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25925         });
25926         
25927         this.editorcore.onRender(ct, position);
25928          
25929         if (this.resizable) {
25930             this.resizeEl = new Roo.Resizable(this.wrap, {
25931                 pinned : true,
25932                 wrap: true,
25933                 dynamic : true,
25934                 minHeight : this.height,
25935                 height: this.height,
25936                 handles : this.resizable,
25937                 width: this.width,
25938                 listeners : {
25939                     resize : function(r, w, h) {
25940                         _t.onResize(w,h); // -something
25941                     }
25942                 }
25943             });
25944             
25945         }
25946         this.createToolbar(this);
25947        
25948         
25949         if(!this.width && this.resizable){
25950             this.setSize(this.wrap.getSize());
25951         }
25952         if (this.resizeEl) {
25953             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25954             // should trigger onReize..
25955         }
25956         
25957     },
25958
25959     // private
25960     onResize : function(w, h)
25961     {
25962         Roo.log('resize: ' +w + ',' + h );
25963         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25964         var ew = false;
25965         var eh = false;
25966         
25967         if(this.inputEl() ){
25968             if(typeof w == 'number'){
25969                 var aw = w - this.wrap.getFrameWidth('lr');
25970                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25971                 ew = aw;
25972             }
25973             if(typeof h == 'number'){
25974                  var tbh = -11;  // fixme it needs to tool bar size!
25975                 for (var i =0; i < this.toolbars.length;i++) {
25976                     // fixme - ask toolbars for heights?
25977                     tbh += this.toolbars[i].el.getHeight();
25978                     //if (this.toolbars[i].footer) {
25979                     //    tbh += this.toolbars[i].footer.el.getHeight();
25980                     //}
25981                 }
25982               
25983                 
25984                 
25985                 
25986                 
25987                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25988                 ah -= 5; // knock a few pixes off for look..
25989                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25990                 var eh = ah;
25991             }
25992         }
25993         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25994         this.editorcore.onResize(ew,eh);
25995         
25996     },
25997
25998     /**
25999      * Toggles the editor between standard and source edit mode.
26000      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26001      */
26002     toggleSourceEdit : function(sourceEditMode)
26003     {
26004         this.editorcore.toggleSourceEdit(sourceEditMode);
26005         
26006         if(this.editorcore.sourceEditMode){
26007             Roo.log('editor - showing textarea');
26008             
26009 //            Roo.log('in');
26010 //            Roo.log(this.syncValue());
26011             this.syncValue();
26012             this.inputEl().removeClass(['hide', 'x-hidden']);
26013             this.inputEl().dom.removeAttribute('tabIndex');
26014             this.inputEl().focus();
26015         }else{
26016             Roo.log('editor - hiding textarea');
26017 //            Roo.log('out')
26018 //            Roo.log(this.pushValue()); 
26019             this.pushValue();
26020             
26021             this.inputEl().addClass(['hide', 'x-hidden']);
26022             this.inputEl().dom.setAttribute('tabIndex', -1);
26023             //this.deferFocus();
26024         }
26025          
26026         if(this.resizable){
26027             this.setSize(this.wrap.getSize());
26028         }
26029         
26030         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26031     },
26032  
26033     // private (for BoxComponent)
26034     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26035
26036     // private (for BoxComponent)
26037     getResizeEl : function(){
26038         return this.wrap;
26039     },
26040
26041     // private (for BoxComponent)
26042     getPositionEl : function(){
26043         return this.wrap;
26044     },
26045
26046     // private
26047     initEvents : function(){
26048         this.originalValue = this.getValue();
26049     },
26050
26051 //    /**
26052 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26053 //     * @method
26054 //     */
26055 //    markInvalid : Roo.emptyFn,
26056 //    /**
26057 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26058 //     * @method
26059 //     */
26060 //    clearInvalid : Roo.emptyFn,
26061
26062     setValue : function(v){
26063         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26064         this.editorcore.pushValue();
26065     },
26066
26067      
26068     // private
26069     deferFocus : function(){
26070         this.focus.defer(10, this);
26071     },
26072
26073     // doc'ed in Field
26074     focus : function(){
26075         this.editorcore.focus();
26076         
26077     },
26078       
26079
26080     // private
26081     onDestroy : function(){
26082         
26083         
26084         
26085         if(this.rendered){
26086             
26087             for (var i =0; i < this.toolbars.length;i++) {
26088                 // fixme - ask toolbars for heights?
26089                 this.toolbars[i].onDestroy();
26090             }
26091             
26092             this.wrap.dom.innerHTML = '';
26093             this.wrap.remove();
26094         }
26095     },
26096
26097     // private
26098     onFirstFocus : function(){
26099         //Roo.log("onFirstFocus");
26100         this.editorcore.onFirstFocus();
26101          for (var i =0; i < this.toolbars.length;i++) {
26102             this.toolbars[i].onFirstFocus();
26103         }
26104         
26105     },
26106     
26107     // private
26108     syncValue : function()
26109     {   
26110         this.editorcore.syncValue();
26111     },
26112     
26113     pushValue : function()
26114     {   
26115         this.editorcore.pushValue();
26116     }
26117      
26118     
26119     // hide stuff that is not compatible
26120     /**
26121      * @event blur
26122      * @hide
26123      */
26124     /**
26125      * @event change
26126      * @hide
26127      */
26128     /**
26129      * @event focus
26130      * @hide
26131      */
26132     /**
26133      * @event specialkey
26134      * @hide
26135      */
26136     /**
26137      * @cfg {String} fieldClass @hide
26138      */
26139     /**
26140      * @cfg {String} focusClass @hide
26141      */
26142     /**
26143      * @cfg {String} autoCreate @hide
26144      */
26145     /**
26146      * @cfg {String} inputType @hide
26147      */
26148      
26149     /**
26150      * @cfg {String} invalidText @hide
26151      */
26152     /**
26153      * @cfg {String} msgFx @hide
26154      */
26155     /**
26156      * @cfg {String} validateOnBlur @hide
26157      */
26158 });
26159  
26160     
26161    
26162    
26163    
26164       
26165 Roo.namespace('Roo.bootstrap.htmleditor');
26166 /**
26167  * @class Roo.bootstrap.HtmlEditorToolbar1
26168  * Basic Toolbar
26169  * 
26170  * @example
26171  * Usage:
26172  *
26173  new Roo.bootstrap.HtmlEditor({
26174     ....
26175     toolbars : [
26176         new Roo.bootstrap.HtmlEditorToolbar1({
26177             disable : { fonts: 1 , format: 1, ..., ... , ...],
26178             btns : [ .... ]
26179         })
26180     }
26181      
26182  * 
26183  * @cfg {Object} disable List of elements to disable..
26184  * @cfg {Array} btns List of additional buttons.
26185  * 
26186  * 
26187  * NEEDS Extra CSS? 
26188  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26189  */
26190  
26191 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26192 {
26193     
26194     Roo.apply(this, config);
26195     
26196     // default disabled, based on 'good practice'..
26197     this.disable = this.disable || {};
26198     Roo.applyIf(this.disable, {
26199         fontSize : true,
26200         colors : true,
26201         specialElements : true
26202     });
26203     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26204     
26205     this.editor = config.editor;
26206     this.editorcore = config.editor.editorcore;
26207     
26208     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26209     
26210     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26211     // dont call parent... till later.
26212 }
26213 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26214      
26215     bar : true,
26216     
26217     editor : false,
26218     editorcore : false,
26219     
26220     
26221     formats : [
26222         "p" ,  
26223         "h1","h2","h3","h4","h5","h6", 
26224         "pre", "code", 
26225         "abbr", "acronym", "address", "cite", "samp", "var",
26226         'div','span'
26227     ],
26228     
26229     onRender : function(ct, position)
26230     {
26231        // Roo.log("Call onRender: " + this.xtype);
26232         
26233        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26234        Roo.log(this.el);
26235        this.el.dom.style.marginBottom = '0';
26236        var _this = this;
26237        var editorcore = this.editorcore;
26238        var editor= this.editor;
26239        
26240        var children = [];
26241        var btn = function(id,cmd , toggle, handler, html){
26242        
26243             var  event = toggle ? 'toggle' : 'click';
26244        
26245             var a = {
26246                 size : 'sm',
26247                 xtype: 'Button',
26248                 xns: Roo.bootstrap,
26249                 //glyphicon : id,
26250                 fa: id,
26251                 cmd : id || cmd,
26252                 enableToggle:toggle !== false,
26253                 html : html || '',
26254                 pressed : toggle ? false : null,
26255                 listeners : {}
26256             };
26257             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26258                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26259             };
26260             children.push(a);
26261             return a;
26262        }
26263        
26264     //    var cb_box = function...
26265         
26266         var style = {
26267                 xtype: 'Button',
26268                 size : 'sm',
26269                 xns: Roo.bootstrap,
26270                 fa : 'font',
26271                 //html : 'submit'
26272                 menu : {
26273                     xtype: 'Menu',
26274                     xns: Roo.bootstrap,
26275                     items:  []
26276                 }
26277         };
26278         Roo.each(this.formats, function(f) {
26279             style.menu.items.push({
26280                 xtype :'MenuItem',
26281                 xns: Roo.bootstrap,
26282                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26283                 tagname : f,
26284                 listeners : {
26285                     click : function()
26286                     {
26287                         editorcore.insertTag(this.tagname);
26288                         editor.focus();
26289                     }
26290                 }
26291                 
26292             });
26293         });
26294         children.push(style);   
26295         
26296         btn('bold',false,true);
26297         btn('italic',false,true);
26298         btn('align-left', 'justifyleft',true);
26299         btn('align-center', 'justifycenter',true);
26300         btn('align-right' , 'justifyright',true);
26301         btn('link', false, false, function(btn) {
26302             //Roo.log("create link?");
26303             var url = prompt(this.createLinkText, this.defaultLinkValue);
26304             if(url && url != 'http:/'+'/'){
26305                 this.editorcore.relayCmd('createlink', url);
26306             }
26307         }),
26308         btn('list','insertunorderedlist',true);
26309         btn('pencil', false,true, function(btn){
26310                 Roo.log(this);
26311                 this.toggleSourceEdit(btn.pressed);
26312         });
26313         
26314         if (this.editor.btns.length > 0) {
26315             for (var i = 0; i<this.editor.btns.length; i++) {
26316                 children.push(this.editor.btns[i]);
26317             }
26318         }
26319         
26320         /*
26321         var cog = {
26322                 xtype: 'Button',
26323                 size : 'sm',
26324                 xns: Roo.bootstrap,
26325                 glyphicon : 'cog',
26326                 //html : 'submit'
26327                 menu : {
26328                     xtype: 'Menu',
26329                     xns: Roo.bootstrap,
26330                     items:  []
26331                 }
26332         };
26333         
26334         cog.menu.items.push({
26335             xtype :'MenuItem',
26336             xns: Roo.bootstrap,
26337             html : Clean styles,
26338             tagname : f,
26339             listeners : {
26340                 click : function()
26341                 {
26342                     editorcore.insertTag(this.tagname);
26343                     editor.focus();
26344                 }
26345             }
26346             
26347         });
26348        */
26349         
26350          
26351        this.xtype = 'NavSimplebar';
26352         
26353         for(var i=0;i< children.length;i++) {
26354             
26355             this.buttons.add(this.addxtypeChild(children[i]));
26356             
26357         }
26358         
26359         editor.on('editorevent', this.updateToolbar, this);
26360     },
26361     onBtnClick : function(id)
26362     {
26363        this.editorcore.relayCmd(id);
26364        this.editorcore.focus();
26365     },
26366     
26367     /**
26368      * Protected method that will not generally be called directly. It triggers
26369      * a toolbar update by reading the markup state of the current selection in the editor.
26370      */
26371     updateToolbar: function(){
26372
26373         if(!this.editorcore.activated){
26374             this.editor.onFirstFocus(); // is this neeed?
26375             return;
26376         }
26377
26378         var btns = this.buttons; 
26379         var doc = this.editorcore.doc;
26380         btns.get('bold').setActive(doc.queryCommandState('bold'));
26381         btns.get('italic').setActive(doc.queryCommandState('italic'));
26382         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26383         
26384         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26385         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26386         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26387         
26388         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26389         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26390          /*
26391         
26392         var ans = this.editorcore.getAllAncestors();
26393         if (this.formatCombo) {
26394             
26395             
26396             var store = this.formatCombo.store;
26397             this.formatCombo.setValue("");
26398             for (var i =0; i < ans.length;i++) {
26399                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26400                     // select it..
26401                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26402                     break;
26403                 }
26404             }
26405         }
26406         
26407         
26408         
26409         // hides menus... - so this cant be on a menu...
26410         Roo.bootstrap.MenuMgr.hideAll();
26411         */
26412         Roo.bootstrap.MenuMgr.hideAll();
26413         //this.editorsyncValue();
26414     },
26415     onFirstFocus: function() {
26416         this.buttons.each(function(item){
26417            item.enable();
26418         });
26419     },
26420     toggleSourceEdit : function(sourceEditMode){
26421         
26422           
26423         if(sourceEditMode){
26424             Roo.log("disabling buttons");
26425            this.buttons.each( function(item){
26426                 if(item.cmd != 'pencil'){
26427                     item.disable();
26428                 }
26429             });
26430           
26431         }else{
26432             Roo.log("enabling buttons");
26433             if(this.editorcore.initialized){
26434                 this.buttons.each( function(item){
26435                     item.enable();
26436                 });
26437             }
26438             
26439         }
26440         Roo.log("calling toggole on editor");
26441         // tell the editor that it's been pressed..
26442         this.editor.toggleSourceEdit(sourceEditMode);
26443        
26444     }
26445 });
26446
26447
26448
26449
26450  
26451 /*
26452  * - LGPL
26453  */
26454
26455 /**
26456  * @class Roo.bootstrap.Markdown
26457  * @extends Roo.bootstrap.TextArea
26458  * Bootstrap Showdown editable area
26459  * @cfg {string} content
26460  * 
26461  * @constructor
26462  * Create a new Showdown
26463  */
26464
26465 Roo.bootstrap.Markdown = function(config){
26466     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26467    
26468 };
26469
26470 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26471     
26472     editing :false,
26473     
26474     initEvents : function()
26475     {
26476         
26477         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26478         this.markdownEl = this.el.createChild({
26479             cls : 'roo-markdown-area'
26480         });
26481         this.inputEl().addClass('d-none');
26482         if (this.getValue() == '') {
26483             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26484             
26485         } else {
26486             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26487         }
26488         this.markdownEl.on('click', this.toggleTextEdit, this);
26489         this.on('blur', this.toggleTextEdit, this);
26490         this.on('specialkey', this.resizeTextArea, this);
26491     },
26492     
26493     toggleTextEdit : function()
26494     {
26495         var sh = this.markdownEl.getHeight();
26496         this.inputEl().addClass('d-none');
26497         this.markdownEl.addClass('d-none');
26498         if (!this.editing) {
26499             // show editor?
26500             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26501             this.inputEl().removeClass('d-none');
26502             this.inputEl().focus();
26503             this.editing = true;
26504             return;
26505         }
26506         // show showdown...
26507         this.updateMarkdown();
26508         this.markdownEl.removeClass('d-none');
26509         this.editing = false;
26510         return;
26511     },
26512     updateMarkdown : function()
26513     {
26514         if (this.getValue() == '') {
26515             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26516             return;
26517         }
26518  
26519         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26520     },
26521     
26522     resizeTextArea: function () {
26523         
26524         var sh = 100;
26525         Roo.log([sh, this.getValue().split("\n").length * 30]);
26526         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26527     },
26528     setValue : function(val)
26529     {
26530         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26531         if (!this.editing) {
26532             this.updateMarkdown();
26533         }
26534         
26535     },
26536     focus : function()
26537     {
26538         if (!this.editing) {
26539             this.toggleTextEdit();
26540         }
26541         
26542     }
26543
26544
26545 });
26546 /**
26547  * @class Roo.bootstrap.Table.AbstractSelectionModel
26548  * @extends Roo.util.Observable
26549  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26550  * implemented by descendant classes.  This class should not be directly instantiated.
26551  * @constructor
26552  */
26553 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26554     this.locked = false;
26555     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26556 };
26557
26558
26559 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26560     /** @ignore Called by the grid automatically. Do not call directly. */
26561     init : function(grid){
26562         this.grid = grid;
26563         this.initEvents();
26564     },
26565
26566     /**
26567      * Locks the selections.
26568      */
26569     lock : function(){
26570         this.locked = true;
26571     },
26572
26573     /**
26574      * Unlocks the selections.
26575      */
26576     unlock : function(){
26577         this.locked = false;
26578     },
26579
26580     /**
26581      * Returns true if the selections are locked.
26582      * @return {Boolean}
26583      */
26584     isLocked : function(){
26585         return this.locked;
26586     },
26587     
26588     
26589     initEvents : function ()
26590     {
26591         
26592     }
26593 });
26594 /**
26595  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26596  * @class Roo.bootstrap.Table.RowSelectionModel
26597  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26598  * It supports multiple selections and keyboard selection/navigation. 
26599  * @constructor
26600  * @param {Object} config
26601  */
26602
26603 Roo.bootstrap.Table.RowSelectionModel = function(config){
26604     Roo.apply(this, config);
26605     this.selections = new Roo.util.MixedCollection(false, function(o){
26606         return o.id;
26607     });
26608
26609     this.last = false;
26610     this.lastActive = false;
26611
26612     this.addEvents({
26613         /**
26614              * @event selectionchange
26615              * Fires when the selection changes
26616              * @param {SelectionModel} this
26617              */
26618             "selectionchange" : true,
26619         /**
26620              * @event afterselectionchange
26621              * Fires after the selection changes (eg. by key press or clicking)
26622              * @param {SelectionModel} this
26623              */
26624             "afterselectionchange" : true,
26625         /**
26626              * @event beforerowselect
26627              * Fires when a row is selected being selected, return false to cancel.
26628              * @param {SelectionModel} this
26629              * @param {Number} rowIndex The selected index
26630              * @param {Boolean} keepExisting False if other selections will be cleared
26631              */
26632             "beforerowselect" : true,
26633         /**
26634              * @event rowselect
26635              * Fires when a row is selected.
26636              * @param {SelectionModel} this
26637              * @param {Number} rowIndex The selected index
26638              * @param {Roo.data.Record} r The record
26639              */
26640             "rowselect" : true,
26641         /**
26642              * @event rowdeselect
26643              * Fires when a row is deselected.
26644              * @param {SelectionModel} this
26645              * @param {Number} rowIndex The selected index
26646              */
26647         "rowdeselect" : true
26648     });
26649     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26650     this.locked = false;
26651  };
26652
26653 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26654     /**
26655      * @cfg {Boolean} singleSelect
26656      * True to allow selection of only one row at a time (defaults to false)
26657      */
26658     singleSelect : false,
26659
26660     // private
26661     initEvents : function()
26662     {
26663
26664         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26665         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26666         //}else{ // allow click to work like normal
26667          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26668         //}
26669         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26670         this.grid.on("rowclick", this.handleMouseDown, this);
26671         
26672         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26673             "up" : function(e){
26674                 if(!e.shiftKey){
26675                     this.selectPrevious(e.shiftKey);
26676                 }else if(this.last !== false && this.lastActive !== false){
26677                     var last = this.last;
26678                     this.selectRange(this.last,  this.lastActive-1);
26679                     this.grid.getView().focusRow(this.lastActive);
26680                     if(last !== false){
26681                         this.last = last;
26682                     }
26683                 }else{
26684                     this.selectFirstRow();
26685                 }
26686                 this.fireEvent("afterselectionchange", this);
26687             },
26688             "down" : function(e){
26689                 if(!e.shiftKey){
26690                     this.selectNext(e.shiftKey);
26691                 }else if(this.last !== false && this.lastActive !== false){
26692                     var last = this.last;
26693                     this.selectRange(this.last,  this.lastActive+1);
26694                     this.grid.getView().focusRow(this.lastActive);
26695                     if(last !== false){
26696                         this.last = last;
26697                     }
26698                 }else{
26699                     this.selectFirstRow();
26700                 }
26701                 this.fireEvent("afterselectionchange", this);
26702             },
26703             scope: this
26704         });
26705         this.grid.store.on('load', function(){
26706             this.selections.clear();
26707         },this);
26708         /*
26709         var view = this.grid.view;
26710         view.on("refresh", this.onRefresh, this);
26711         view.on("rowupdated", this.onRowUpdated, this);
26712         view.on("rowremoved", this.onRemove, this);
26713         */
26714     },
26715
26716     // private
26717     onRefresh : function()
26718     {
26719         var ds = this.grid.store, i, v = this.grid.view;
26720         var s = this.selections;
26721         s.each(function(r){
26722             if((i = ds.indexOfId(r.id)) != -1){
26723                 v.onRowSelect(i);
26724             }else{
26725                 s.remove(r);
26726             }
26727         });
26728     },
26729
26730     // private
26731     onRemove : function(v, index, r){
26732         this.selections.remove(r);
26733     },
26734
26735     // private
26736     onRowUpdated : function(v, index, r){
26737         if(this.isSelected(r)){
26738             v.onRowSelect(index);
26739         }
26740     },
26741
26742     /**
26743      * Select records.
26744      * @param {Array} records The records to select
26745      * @param {Boolean} keepExisting (optional) True to keep existing selections
26746      */
26747     selectRecords : function(records, keepExisting)
26748     {
26749         if(!keepExisting){
26750             this.clearSelections();
26751         }
26752             var ds = this.grid.store;
26753         for(var i = 0, len = records.length; i < len; i++){
26754             this.selectRow(ds.indexOf(records[i]), true);
26755         }
26756     },
26757
26758     /**
26759      * Gets the number of selected rows.
26760      * @return {Number}
26761      */
26762     getCount : function(){
26763         return this.selections.length;
26764     },
26765
26766     /**
26767      * Selects the first row in the grid.
26768      */
26769     selectFirstRow : function(){
26770         this.selectRow(0);
26771     },
26772
26773     /**
26774      * Select the last row.
26775      * @param {Boolean} keepExisting (optional) True to keep existing selections
26776      */
26777     selectLastRow : function(keepExisting){
26778         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26779         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26780     },
26781
26782     /**
26783      * Selects the row immediately following the last selected row.
26784      * @param {Boolean} keepExisting (optional) True to keep existing selections
26785      */
26786     selectNext : function(keepExisting)
26787     {
26788             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26789             this.selectRow(this.last+1, keepExisting);
26790             this.grid.getView().focusRow(this.last);
26791         }
26792     },
26793
26794     /**
26795      * Selects the row that precedes the last selected row.
26796      * @param {Boolean} keepExisting (optional) True to keep existing selections
26797      */
26798     selectPrevious : function(keepExisting){
26799         if(this.last){
26800             this.selectRow(this.last-1, keepExisting);
26801             this.grid.getView().focusRow(this.last);
26802         }
26803     },
26804
26805     /**
26806      * Returns the selected records
26807      * @return {Array} Array of selected records
26808      */
26809     getSelections : function(){
26810         return [].concat(this.selections.items);
26811     },
26812
26813     /**
26814      * Returns the first selected record.
26815      * @return {Record}
26816      */
26817     getSelected : function(){
26818         return this.selections.itemAt(0);
26819     },
26820
26821
26822     /**
26823      * Clears all selections.
26824      */
26825     clearSelections : function(fast)
26826     {
26827         if(this.locked) {
26828             return;
26829         }
26830         if(fast !== true){
26831                 var ds = this.grid.store;
26832             var s = this.selections;
26833             s.each(function(r){
26834                 this.deselectRow(ds.indexOfId(r.id));
26835             }, this);
26836             s.clear();
26837         }else{
26838             this.selections.clear();
26839         }
26840         this.last = false;
26841     },
26842
26843
26844     /**
26845      * Selects all rows.
26846      */
26847     selectAll : function(){
26848         if(this.locked) {
26849             return;
26850         }
26851         this.selections.clear();
26852         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26853             this.selectRow(i, true);
26854         }
26855     },
26856
26857     /**
26858      * Returns True if there is a selection.
26859      * @return {Boolean}
26860      */
26861     hasSelection : function(){
26862         return this.selections.length > 0;
26863     },
26864
26865     /**
26866      * Returns True if the specified row is selected.
26867      * @param {Number/Record} record The record or index of the record to check
26868      * @return {Boolean}
26869      */
26870     isSelected : function(index){
26871             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26872         return (r && this.selections.key(r.id) ? true : false);
26873     },
26874
26875     /**
26876      * Returns True if the specified record id is selected.
26877      * @param {String} id The id of record to check
26878      * @return {Boolean}
26879      */
26880     isIdSelected : function(id){
26881         return (this.selections.key(id) ? true : false);
26882     },
26883
26884
26885     // private
26886     handleMouseDBClick : function(e, t){
26887         
26888     },
26889     // private
26890     handleMouseDown : function(e, t)
26891     {
26892             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26893         if(this.isLocked() || rowIndex < 0 ){
26894             return;
26895         };
26896         if(e.shiftKey && this.last !== false){
26897             var last = this.last;
26898             this.selectRange(last, rowIndex, e.ctrlKey);
26899             this.last = last; // reset the last
26900             t.focus();
26901     
26902         }else{
26903             var isSelected = this.isSelected(rowIndex);
26904             //Roo.log("select row:" + rowIndex);
26905             if(isSelected){
26906                 this.deselectRow(rowIndex);
26907             } else {
26908                         this.selectRow(rowIndex, true);
26909             }
26910     
26911             /*
26912                 if(e.button !== 0 && isSelected){
26913                 alert('rowIndex 2: ' + rowIndex);
26914                     view.focusRow(rowIndex);
26915                 }else if(e.ctrlKey && isSelected){
26916                     this.deselectRow(rowIndex);
26917                 }else if(!isSelected){
26918                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26919                     view.focusRow(rowIndex);
26920                 }
26921             */
26922         }
26923         this.fireEvent("afterselectionchange", this);
26924     },
26925     // private
26926     handleDragableRowClick :  function(grid, rowIndex, e) 
26927     {
26928         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26929             this.selectRow(rowIndex, false);
26930             grid.view.focusRow(rowIndex);
26931              this.fireEvent("afterselectionchange", this);
26932         }
26933     },
26934     
26935     /**
26936      * Selects multiple rows.
26937      * @param {Array} rows Array of the indexes of the row to select
26938      * @param {Boolean} keepExisting (optional) True to keep existing selections
26939      */
26940     selectRows : function(rows, keepExisting){
26941         if(!keepExisting){
26942             this.clearSelections();
26943         }
26944         for(var i = 0, len = rows.length; i < len; i++){
26945             this.selectRow(rows[i], true);
26946         }
26947     },
26948
26949     /**
26950      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26951      * @param {Number} startRow The index of the first row in the range
26952      * @param {Number} endRow The index of the last row in the range
26953      * @param {Boolean} keepExisting (optional) True to retain existing selections
26954      */
26955     selectRange : function(startRow, endRow, keepExisting){
26956         if(this.locked) {
26957             return;
26958         }
26959         if(!keepExisting){
26960             this.clearSelections();
26961         }
26962         if(startRow <= endRow){
26963             for(var i = startRow; i <= endRow; i++){
26964                 this.selectRow(i, true);
26965             }
26966         }else{
26967             for(var i = startRow; i >= endRow; i--){
26968                 this.selectRow(i, true);
26969             }
26970         }
26971     },
26972
26973     /**
26974      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26975      * @param {Number} startRow The index of the first row in the range
26976      * @param {Number} endRow The index of the last row in the range
26977      */
26978     deselectRange : function(startRow, endRow, preventViewNotify){
26979         if(this.locked) {
26980             return;
26981         }
26982         for(var i = startRow; i <= endRow; i++){
26983             this.deselectRow(i, preventViewNotify);
26984         }
26985     },
26986
26987     /**
26988      * Selects a row.
26989      * @param {Number} row The index of the row to select
26990      * @param {Boolean} keepExisting (optional) True to keep existing selections
26991      */
26992     selectRow : function(index, keepExisting, preventViewNotify)
26993     {
26994             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26995             return;
26996         }
26997         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26998             if(!keepExisting || this.singleSelect){
26999                 this.clearSelections();
27000             }
27001             
27002             var r = this.grid.store.getAt(index);
27003             //console.log('selectRow - record id :' + r.id);
27004             
27005             this.selections.add(r);
27006             this.last = this.lastActive = index;
27007             if(!preventViewNotify){
27008                 var proxy = new Roo.Element(
27009                                 this.grid.getRowDom(index)
27010                 );
27011                 proxy.addClass('bg-info info');
27012             }
27013             this.fireEvent("rowselect", this, index, r);
27014             this.fireEvent("selectionchange", this);
27015         }
27016     },
27017
27018     /**
27019      * Deselects a row.
27020      * @param {Number} row The index of the row to deselect
27021      */
27022     deselectRow : function(index, preventViewNotify)
27023     {
27024         if(this.locked) {
27025             return;
27026         }
27027         if(this.last == index){
27028             this.last = false;
27029         }
27030         if(this.lastActive == index){
27031             this.lastActive = false;
27032         }
27033         
27034         var r = this.grid.store.getAt(index);
27035         if (!r) {
27036             return;
27037         }
27038         
27039         this.selections.remove(r);
27040         //.console.log('deselectRow - record id :' + r.id);
27041         if(!preventViewNotify){
27042         
27043             var proxy = new Roo.Element(
27044                 this.grid.getRowDom(index)
27045             );
27046             proxy.removeClass('bg-info info');
27047         }
27048         this.fireEvent("rowdeselect", this, index);
27049         this.fireEvent("selectionchange", this);
27050     },
27051
27052     // private
27053     restoreLast : function(){
27054         if(this._last){
27055             this.last = this._last;
27056         }
27057     },
27058
27059     // private
27060     acceptsNav : function(row, col, cm){
27061         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27062     },
27063
27064     // private
27065     onEditorKey : function(field, e){
27066         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27067         if(k == e.TAB){
27068             e.stopEvent();
27069             ed.completeEdit();
27070             if(e.shiftKey){
27071                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27072             }else{
27073                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27074             }
27075         }else if(k == e.ENTER && !e.ctrlKey){
27076             e.stopEvent();
27077             ed.completeEdit();
27078             if(e.shiftKey){
27079                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27080             }else{
27081                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27082             }
27083         }else if(k == e.ESC){
27084             ed.cancelEdit();
27085         }
27086         if(newCell){
27087             g.startEditing(newCell[0], newCell[1]);
27088         }
27089     }
27090 });
27091 /*
27092  * Based on:
27093  * Ext JS Library 1.1.1
27094  * Copyright(c) 2006-2007, Ext JS, LLC.
27095  *
27096  * Originally Released Under LGPL - original licence link has changed is not relivant.
27097  *
27098  * Fork - LGPL
27099  * <script type="text/javascript">
27100  */
27101  
27102 /**
27103  * @class Roo.bootstrap.PagingToolbar
27104  * @extends Roo.bootstrap.NavSimplebar
27105  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27106  * @constructor
27107  * Create a new PagingToolbar
27108  * @param {Object} config The config object
27109  * @param {Roo.data.Store} store
27110  */
27111 Roo.bootstrap.PagingToolbar = function(config)
27112 {
27113     // old args format still supported... - xtype is prefered..
27114         // created from xtype...
27115     
27116     this.ds = config.dataSource;
27117     
27118     if (config.store && !this.ds) {
27119         this.store= Roo.factory(config.store, Roo.data);
27120         this.ds = this.store;
27121         this.ds.xmodule = this.xmodule || false;
27122     }
27123     
27124     this.toolbarItems = [];
27125     if (config.items) {
27126         this.toolbarItems = config.items;
27127     }
27128     
27129     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27130     
27131     this.cursor = 0;
27132     
27133     if (this.ds) { 
27134         this.bind(this.ds);
27135     }
27136     
27137     if (Roo.bootstrap.version == 4) {
27138         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27139     } else {
27140         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27141     }
27142     
27143 };
27144
27145 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27146     /**
27147      * @cfg {Roo.data.Store} dataSource
27148      * The underlying data store providing the paged data
27149      */
27150     /**
27151      * @cfg {String/HTMLElement/Element} container
27152      * container The id or element that will contain the toolbar
27153      */
27154     /**
27155      * @cfg {Boolean} displayInfo
27156      * True to display the displayMsg (defaults to false)
27157      */
27158     /**
27159      * @cfg {Number} pageSize
27160      * The number of records to display per page (defaults to 20)
27161      */
27162     pageSize: 20,
27163     /**
27164      * @cfg {String} displayMsg
27165      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27166      */
27167     displayMsg : 'Displaying {0} - {1} of {2}',
27168     /**
27169      * @cfg {String} emptyMsg
27170      * The message to display when no records are found (defaults to "No data to display")
27171      */
27172     emptyMsg : 'No data to display',
27173     /**
27174      * Customizable piece of the default paging text (defaults to "Page")
27175      * @type String
27176      */
27177     beforePageText : "Page",
27178     /**
27179      * Customizable piece of the default paging text (defaults to "of %0")
27180      * @type String
27181      */
27182     afterPageText : "of {0}",
27183     /**
27184      * Customizable piece of the default paging text (defaults to "First Page")
27185      * @type String
27186      */
27187     firstText : "First Page",
27188     /**
27189      * Customizable piece of the default paging text (defaults to "Previous Page")
27190      * @type String
27191      */
27192     prevText : "Previous Page",
27193     /**
27194      * Customizable piece of the default paging text (defaults to "Next Page")
27195      * @type String
27196      */
27197     nextText : "Next Page",
27198     /**
27199      * Customizable piece of the default paging text (defaults to "Last Page")
27200      * @type String
27201      */
27202     lastText : "Last Page",
27203     /**
27204      * Customizable piece of the default paging text (defaults to "Refresh")
27205      * @type String
27206      */
27207     refreshText : "Refresh",
27208
27209     buttons : false,
27210     // private
27211     onRender : function(ct, position) 
27212     {
27213         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27214         this.navgroup.parentId = this.id;
27215         this.navgroup.onRender(this.el, null);
27216         // add the buttons to the navgroup
27217         
27218         if(this.displayInfo){
27219             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27220             this.displayEl = this.el.select('.x-paging-info', true).first();
27221 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27222 //            this.displayEl = navel.el.select('span',true).first();
27223         }
27224         
27225         var _this = this;
27226         
27227         if(this.buttons){
27228             Roo.each(_this.buttons, function(e){ // this might need to use render????
27229                Roo.factory(e).render(_this.el);
27230             });
27231         }
27232             
27233         Roo.each(_this.toolbarItems, function(e) {
27234             _this.navgroup.addItem(e);
27235         });
27236         
27237         
27238         this.first = this.navgroup.addItem({
27239             tooltip: this.firstText,
27240             cls: "prev btn-outline-secondary",
27241             html : ' <i class="fa fa-step-backward"></i>',
27242             disabled: true,
27243             preventDefault: true,
27244             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27245         });
27246         
27247         this.prev =  this.navgroup.addItem({
27248             tooltip: this.prevText,
27249             cls: "prev btn-outline-secondary",
27250             html : ' <i class="fa fa-backward"></i>',
27251             disabled: true,
27252             preventDefault: true,
27253             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27254         });
27255     //this.addSeparator();
27256         
27257         
27258         var field = this.navgroup.addItem( {
27259             tagtype : 'span',
27260             cls : 'x-paging-position  btn-outline-secondary',
27261              disabled: true,
27262             html : this.beforePageText  +
27263                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27264                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27265          } ); //?? escaped?
27266         
27267         this.field = field.el.select('input', true).first();
27268         this.field.on("keydown", this.onPagingKeydown, this);
27269         this.field.on("focus", function(){this.dom.select();});
27270     
27271     
27272         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27273         //this.field.setHeight(18);
27274         //this.addSeparator();
27275         this.next = this.navgroup.addItem({
27276             tooltip: this.nextText,
27277             cls: "next btn-outline-secondary",
27278             html : ' <i class="fa fa-forward"></i>',
27279             disabled: true,
27280             preventDefault: true,
27281             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27282         });
27283         this.last = this.navgroup.addItem({
27284             tooltip: this.lastText,
27285             html : ' <i class="fa fa-step-forward"></i>',
27286             cls: "next btn-outline-secondary",
27287             disabled: true,
27288             preventDefault: true,
27289             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27290         });
27291     //this.addSeparator();
27292         this.loading = this.navgroup.addItem({
27293             tooltip: this.refreshText,
27294             cls: "btn-outline-secondary",
27295             html : ' <i class="fa fa-refresh"></i>',
27296             preventDefault: true,
27297             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27298         });
27299         
27300     },
27301
27302     // private
27303     updateInfo : function(){
27304         if(this.displayEl){
27305             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27306             var msg = count == 0 ?
27307                 this.emptyMsg :
27308                 String.format(
27309                     this.displayMsg,
27310                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27311                 );
27312             this.displayEl.update(msg);
27313         }
27314     },
27315
27316     // private
27317     onLoad : function(ds, r, o)
27318     {
27319         this.cursor = o.params && o.params.start ? o.params.start : 0;
27320         
27321         var d = this.getPageData(),
27322             ap = d.activePage,
27323             ps = d.pages;
27324         
27325         
27326         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27327         this.field.dom.value = ap;
27328         this.first.setDisabled(ap == 1);
27329         this.prev.setDisabled(ap == 1);
27330         this.next.setDisabled(ap == ps);
27331         this.last.setDisabled(ap == ps);
27332         this.loading.enable();
27333         this.updateInfo();
27334     },
27335
27336     // private
27337     getPageData : function(){
27338         var total = this.ds.getTotalCount();
27339         return {
27340             total : total,
27341             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27342             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27343         };
27344     },
27345
27346     // private
27347     onLoadError : function(){
27348         this.loading.enable();
27349     },
27350
27351     // private
27352     onPagingKeydown : function(e){
27353         var k = e.getKey();
27354         var d = this.getPageData();
27355         if(k == e.RETURN){
27356             var v = this.field.dom.value, pageNum;
27357             if(!v || isNaN(pageNum = parseInt(v, 10))){
27358                 this.field.dom.value = d.activePage;
27359                 return;
27360             }
27361             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27362             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27363             e.stopEvent();
27364         }
27365         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))
27366         {
27367           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27368           this.field.dom.value = pageNum;
27369           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27370           e.stopEvent();
27371         }
27372         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27373         {
27374           var v = this.field.dom.value, pageNum; 
27375           var increment = (e.shiftKey) ? 10 : 1;
27376           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27377                 increment *= -1;
27378           }
27379           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27380             this.field.dom.value = d.activePage;
27381             return;
27382           }
27383           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27384           {
27385             this.field.dom.value = parseInt(v, 10) + increment;
27386             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27387             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27388           }
27389           e.stopEvent();
27390         }
27391     },
27392
27393     // private
27394     beforeLoad : function(){
27395         if(this.loading){
27396             this.loading.disable();
27397         }
27398     },
27399
27400     // private
27401     onClick : function(which){
27402         
27403         var ds = this.ds;
27404         if (!ds) {
27405             return;
27406         }
27407         
27408         switch(which){
27409             case "first":
27410                 ds.load({params:{start: 0, limit: this.pageSize}});
27411             break;
27412             case "prev":
27413                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27414             break;
27415             case "next":
27416                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27417             break;
27418             case "last":
27419                 var total = ds.getTotalCount();
27420                 var extra = total % this.pageSize;
27421                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27422                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27423             break;
27424             case "refresh":
27425                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27426             break;
27427         }
27428     },
27429
27430     /**
27431      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27432      * @param {Roo.data.Store} store The data store to unbind
27433      */
27434     unbind : function(ds){
27435         ds.un("beforeload", this.beforeLoad, this);
27436         ds.un("load", this.onLoad, this);
27437         ds.un("loadexception", this.onLoadError, this);
27438         ds.un("remove", this.updateInfo, this);
27439         ds.un("add", this.updateInfo, this);
27440         this.ds = undefined;
27441     },
27442
27443     /**
27444      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27445      * @param {Roo.data.Store} store The data store to bind
27446      */
27447     bind : function(ds){
27448         ds.on("beforeload", this.beforeLoad, this);
27449         ds.on("load", this.onLoad, this);
27450         ds.on("loadexception", this.onLoadError, this);
27451         ds.on("remove", this.updateInfo, this);
27452         ds.on("add", this.updateInfo, this);
27453         this.ds = ds;
27454     }
27455 });/*
27456  * - LGPL
27457  *
27458  * element
27459  * 
27460  */
27461
27462 /**
27463  * @class Roo.bootstrap.MessageBar
27464  * @extends Roo.bootstrap.Component
27465  * Bootstrap MessageBar class
27466  * @cfg {String} html contents of the MessageBar
27467  * @cfg {String} weight (info | success | warning | danger) default info
27468  * @cfg {String} beforeClass insert the bar before the given class
27469  * @cfg {Boolean} closable (true | false) default false
27470  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27471  * 
27472  * @constructor
27473  * Create a new Element
27474  * @param {Object} config The config object
27475  */
27476
27477 Roo.bootstrap.MessageBar = function(config){
27478     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27479 };
27480
27481 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27482     
27483     html: '',
27484     weight: 'info',
27485     closable: false,
27486     fixed: false,
27487     beforeClass: 'bootstrap-sticky-wrap',
27488     
27489     getAutoCreate : function(){
27490         
27491         var cfg = {
27492             tag: 'div',
27493             cls: 'alert alert-dismissable alert-' + this.weight,
27494             cn: [
27495                 {
27496                     tag: 'span',
27497                     cls: 'message',
27498                     html: this.html || ''
27499                 }
27500             ]
27501         };
27502         
27503         if(this.fixed){
27504             cfg.cls += ' alert-messages-fixed';
27505         }
27506         
27507         if(this.closable){
27508             cfg.cn.push({
27509                 tag: 'button',
27510                 cls: 'close',
27511                 html: 'x'
27512             });
27513         }
27514         
27515         return cfg;
27516     },
27517     
27518     onRender : function(ct, position)
27519     {
27520         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27521         
27522         if(!this.el){
27523             var cfg = Roo.apply({},  this.getAutoCreate());
27524             cfg.id = Roo.id();
27525             
27526             if (this.cls) {
27527                 cfg.cls += ' ' + this.cls;
27528             }
27529             if (this.style) {
27530                 cfg.style = this.style;
27531             }
27532             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27533             
27534             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27535         }
27536         
27537         this.el.select('>button.close').on('click', this.hide, this);
27538         
27539     },
27540     
27541     show : function()
27542     {
27543         if (!this.rendered) {
27544             this.render();
27545         }
27546         
27547         this.el.show();
27548         
27549         this.fireEvent('show', this);
27550         
27551     },
27552     
27553     hide : function()
27554     {
27555         if (!this.rendered) {
27556             this.render();
27557         }
27558         
27559         this.el.hide();
27560         
27561         this.fireEvent('hide', this);
27562     },
27563     
27564     update : function()
27565     {
27566 //        var e = this.el.dom.firstChild;
27567 //        
27568 //        if(this.closable){
27569 //            e = e.nextSibling;
27570 //        }
27571 //        
27572 //        e.data = this.html || '';
27573
27574         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27575     }
27576    
27577 });
27578
27579  
27580
27581      /*
27582  * - LGPL
27583  *
27584  * Graph
27585  * 
27586  */
27587
27588
27589 /**
27590  * @class Roo.bootstrap.Graph
27591  * @extends Roo.bootstrap.Component
27592  * Bootstrap Graph class
27593 > Prameters
27594  -sm {number} sm 4
27595  -md {number} md 5
27596  @cfg {String} graphtype  bar | vbar | pie
27597  @cfg {number} g_x coodinator | centre x (pie)
27598  @cfg {number} g_y coodinator | centre y (pie)
27599  @cfg {number} g_r radius (pie)
27600  @cfg {number} g_height height of the chart (respected by all elements in the set)
27601  @cfg {number} g_width width of the chart (respected by all elements in the set)
27602  @cfg {Object} title The title of the chart
27603     
27604  -{Array}  values
27605  -opts (object) options for the chart 
27606      o {
27607      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27608      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27609      o vgutter (number)
27610      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.
27611      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27612      o to
27613      o stretch (boolean)
27614      o }
27615  -opts (object) options for the pie
27616      o{
27617      o cut
27618      o startAngle (number)
27619      o endAngle (number)
27620      } 
27621  *
27622  * @constructor
27623  * Create a new Input
27624  * @param {Object} config The config object
27625  */
27626
27627 Roo.bootstrap.Graph = function(config){
27628     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27629     
27630     this.addEvents({
27631         // img events
27632         /**
27633          * @event click
27634          * The img click event for the img.
27635          * @param {Roo.EventObject} e
27636          */
27637         "click" : true
27638     });
27639 };
27640
27641 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27642     
27643     sm: 4,
27644     md: 5,
27645     graphtype: 'bar',
27646     g_height: 250,
27647     g_width: 400,
27648     g_x: 50,
27649     g_y: 50,
27650     g_r: 30,
27651     opts:{
27652         //g_colors: this.colors,
27653         g_type: 'soft',
27654         g_gutter: '20%'
27655
27656     },
27657     title : false,
27658
27659     getAutoCreate : function(){
27660         
27661         var cfg = {
27662             tag: 'div',
27663             html : null
27664         };
27665         
27666         
27667         return  cfg;
27668     },
27669
27670     onRender : function(ct,position){
27671         
27672         
27673         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27674         
27675         if (typeof(Raphael) == 'undefined') {
27676             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27677             return;
27678         }
27679         
27680         this.raphael = Raphael(this.el.dom);
27681         
27682                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27683                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27684                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27685                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27686                 /*
27687                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27688                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27689                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27690                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27691                 
27692                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27693                 r.barchart(330, 10, 300, 220, data1);
27694                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27695                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27696                 */
27697                 
27698                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27699                 // r.barchart(30, 30, 560, 250,  xdata, {
27700                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27701                 //     axis : "0 0 1 1",
27702                 //     axisxlabels :  xdata
27703                 //     //yvalues : cols,
27704                    
27705                 // });
27706 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27707 //        
27708 //        this.load(null,xdata,{
27709 //                axis : "0 0 1 1",
27710 //                axisxlabels :  xdata
27711 //                });
27712
27713     },
27714
27715     load : function(graphtype,xdata,opts)
27716     {
27717         this.raphael.clear();
27718         if(!graphtype) {
27719             graphtype = this.graphtype;
27720         }
27721         if(!opts){
27722             opts = this.opts;
27723         }
27724         var r = this.raphael,
27725             fin = function () {
27726                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27727             },
27728             fout = function () {
27729                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27730             },
27731             pfin = function() {
27732                 this.sector.stop();
27733                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27734
27735                 if (this.label) {
27736                     this.label[0].stop();
27737                     this.label[0].attr({ r: 7.5 });
27738                     this.label[1].attr({ "font-weight": 800 });
27739                 }
27740             },
27741             pfout = function() {
27742                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27743
27744                 if (this.label) {
27745                     this.label[0].animate({ r: 5 }, 500, "bounce");
27746                     this.label[1].attr({ "font-weight": 400 });
27747                 }
27748             };
27749
27750         switch(graphtype){
27751             case 'bar':
27752                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27753                 break;
27754             case 'hbar':
27755                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27756                 break;
27757             case 'pie':
27758 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27759 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27760 //            
27761                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27762                 
27763                 break;
27764
27765         }
27766         
27767         if(this.title){
27768             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27769         }
27770         
27771     },
27772     
27773     setTitle: function(o)
27774     {
27775         this.title = o;
27776     },
27777     
27778     initEvents: function() {
27779         
27780         if(!this.href){
27781             this.el.on('click', this.onClick, this);
27782         }
27783     },
27784     
27785     onClick : function(e)
27786     {
27787         Roo.log('img onclick');
27788         this.fireEvent('click', this, e);
27789     }
27790    
27791 });
27792
27793  
27794 /*
27795  * - LGPL
27796  *
27797  * numberBox
27798  * 
27799  */
27800 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27801
27802 /**
27803  * @class Roo.bootstrap.dash.NumberBox
27804  * @extends Roo.bootstrap.Component
27805  * Bootstrap NumberBox class
27806  * @cfg {String} headline Box headline
27807  * @cfg {String} content Box content
27808  * @cfg {String} icon Box icon
27809  * @cfg {String} footer Footer text
27810  * @cfg {String} fhref Footer href
27811  * 
27812  * @constructor
27813  * Create a new NumberBox
27814  * @param {Object} config The config object
27815  */
27816
27817
27818 Roo.bootstrap.dash.NumberBox = function(config){
27819     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27820     
27821 };
27822
27823 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27824     
27825     headline : '',
27826     content : '',
27827     icon : '',
27828     footer : '',
27829     fhref : '',
27830     ficon : '',
27831     
27832     getAutoCreate : function(){
27833         
27834         var cfg = {
27835             tag : 'div',
27836             cls : 'small-box ',
27837             cn : [
27838                 {
27839                     tag : 'div',
27840                     cls : 'inner',
27841                     cn :[
27842                         {
27843                             tag : 'h3',
27844                             cls : 'roo-headline',
27845                             html : this.headline
27846                         },
27847                         {
27848                             tag : 'p',
27849                             cls : 'roo-content',
27850                             html : this.content
27851                         }
27852                     ]
27853                 }
27854             ]
27855         };
27856         
27857         if(this.icon){
27858             cfg.cn.push({
27859                 tag : 'div',
27860                 cls : 'icon',
27861                 cn :[
27862                     {
27863                         tag : 'i',
27864                         cls : 'ion ' + this.icon
27865                     }
27866                 ]
27867             });
27868         }
27869         
27870         if(this.footer){
27871             var footer = {
27872                 tag : 'a',
27873                 cls : 'small-box-footer',
27874                 href : this.fhref || '#',
27875                 html : this.footer
27876             };
27877             
27878             cfg.cn.push(footer);
27879             
27880         }
27881         
27882         return  cfg;
27883     },
27884
27885     onRender : function(ct,position){
27886         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27887
27888
27889        
27890                 
27891     },
27892
27893     setHeadline: function (value)
27894     {
27895         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27896     },
27897     
27898     setFooter: function (value, href)
27899     {
27900         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27901         
27902         if(href){
27903             this.el.select('a.small-box-footer',true).first().attr('href', href);
27904         }
27905         
27906     },
27907
27908     setContent: function (value)
27909     {
27910         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27911     },
27912
27913     initEvents: function() 
27914     {   
27915         
27916     }
27917     
27918 });
27919
27920  
27921 /*
27922  * - LGPL
27923  *
27924  * TabBox
27925  * 
27926  */
27927 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27928
27929 /**
27930  * @class Roo.bootstrap.dash.TabBox
27931  * @extends Roo.bootstrap.Component
27932  * Bootstrap TabBox class
27933  * @cfg {String} title Title of the TabBox
27934  * @cfg {String} icon Icon of the TabBox
27935  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27936  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27937  * 
27938  * @constructor
27939  * Create a new TabBox
27940  * @param {Object} config The config object
27941  */
27942
27943
27944 Roo.bootstrap.dash.TabBox = function(config){
27945     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27946     this.addEvents({
27947         // raw events
27948         /**
27949          * @event addpane
27950          * When a pane is added
27951          * @param {Roo.bootstrap.dash.TabPane} pane
27952          */
27953         "addpane" : true,
27954         /**
27955          * @event activatepane
27956          * When a pane is activated
27957          * @param {Roo.bootstrap.dash.TabPane} pane
27958          */
27959         "activatepane" : true
27960         
27961          
27962     });
27963     
27964     this.panes = [];
27965 };
27966
27967 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27968
27969     title : '',
27970     icon : false,
27971     showtabs : true,
27972     tabScrollable : false,
27973     
27974     getChildContainer : function()
27975     {
27976         return this.el.select('.tab-content', true).first();
27977     },
27978     
27979     getAutoCreate : function(){
27980         
27981         var header = {
27982             tag: 'li',
27983             cls: 'pull-left header',
27984             html: this.title,
27985             cn : []
27986         };
27987         
27988         if(this.icon){
27989             header.cn.push({
27990                 tag: 'i',
27991                 cls: 'fa ' + this.icon
27992             });
27993         }
27994         
27995         var h = {
27996             tag: 'ul',
27997             cls: 'nav nav-tabs pull-right',
27998             cn: [
27999                 header
28000             ]
28001         };
28002         
28003         if(this.tabScrollable){
28004             h = {
28005                 tag: 'div',
28006                 cls: 'tab-header',
28007                 cn: [
28008                     {
28009                         tag: 'ul',
28010                         cls: 'nav nav-tabs pull-right',
28011                         cn: [
28012                             header
28013                         ]
28014                     }
28015                 ]
28016             };
28017         }
28018         
28019         var cfg = {
28020             tag: 'div',
28021             cls: 'nav-tabs-custom',
28022             cn: [
28023                 h,
28024                 {
28025                     tag: 'div',
28026                     cls: 'tab-content no-padding',
28027                     cn: []
28028                 }
28029             ]
28030         };
28031
28032         return  cfg;
28033     },
28034     initEvents : function()
28035     {
28036         //Roo.log('add add pane handler');
28037         this.on('addpane', this.onAddPane, this);
28038     },
28039      /**
28040      * Updates the box title
28041      * @param {String} html to set the title to.
28042      */
28043     setTitle : function(value)
28044     {
28045         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28046     },
28047     onAddPane : function(pane)
28048     {
28049         this.panes.push(pane);
28050         //Roo.log('addpane');
28051         //Roo.log(pane);
28052         // tabs are rendere left to right..
28053         if(!this.showtabs){
28054             return;
28055         }
28056         
28057         var ctr = this.el.select('.nav-tabs', true).first();
28058          
28059          
28060         var existing = ctr.select('.nav-tab',true);
28061         var qty = existing.getCount();;
28062         
28063         
28064         var tab = ctr.createChild({
28065             tag : 'li',
28066             cls : 'nav-tab' + (qty ? '' : ' active'),
28067             cn : [
28068                 {
28069                     tag : 'a',
28070                     href:'#',
28071                     html : pane.title
28072                 }
28073             ]
28074         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28075         pane.tab = tab;
28076         
28077         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28078         if (!qty) {
28079             pane.el.addClass('active');
28080         }
28081         
28082                 
28083     },
28084     onTabClick : function(ev,un,ob,pane)
28085     {
28086         //Roo.log('tab - prev default');
28087         ev.preventDefault();
28088         
28089         
28090         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28091         pane.tab.addClass('active');
28092         //Roo.log(pane.title);
28093         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28094         // technically we should have a deactivate event.. but maybe add later.
28095         // and it should not de-activate the selected tab...
28096         this.fireEvent('activatepane', pane);
28097         pane.el.addClass('active');
28098         pane.fireEvent('activate');
28099         
28100         
28101     },
28102     
28103     getActivePane : function()
28104     {
28105         var r = false;
28106         Roo.each(this.panes, function(p) {
28107             if(p.el.hasClass('active')){
28108                 r = p;
28109                 return false;
28110             }
28111             
28112             return;
28113         });
28114         
28115         return r;
28116     }
28117     
28118     
28119 });
28120
28121  
28122 /*
28123  * - LGPL
28124  *
28125  * Tab pane
28126  * 
28127  */
28128 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28129 /**
28130  * @class Roo.bootstrap.TabPane
28131  * @extends Roo.bootstrap.Component
28132  * Bootstrap TabPane class
28133  * @cfg {Boolean} active (false | true) Default false
28134  * @cfg {String} title title of panel
28135
28136  * 
28137  * @constructor
28138  * Create a new TabPane
28139  * @param {Object} config The config object
28140  */
28141
28142 Roo.bootstrap.dash.TabPane = function(config){
28143     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28144     
28145     this.addEvents({
28146         // raw events
28147         /**
28148          * @event activate
28149          * When a pane is activated
28150          * @param {Roo.bootstrap.dash.TabPane} pane
28151          */
28152         "activate" : true
28153          
28154     });
28155 };
28156
28157 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28158     
28159     active : false,
28160     title : '',
28161     
28162     // the tabBox that this is attached to.
28163     tab : false,
28164      
28165     getAutoCreate : function() 
28166     {
28167         var cfg = {
28168             tag: 'div',
28169             cls: 'tab-pane'
28170         };
28171         
28172         if(this.active){
28173             cfg.cls += ' active';
28174         }
28175         
28176         return cfg;
28177     },
28178     initEvents  : function()
28179     {
28180         //Roo.log('trigger add pane handler');
28181         this.parent().fireEvent('addpane', this)
28182     },
28183     
28184      /**
28185      * Updates the tab title 
28186      * @param {String} html to set the title to.
28187      */
28188     setTitle: function(str)
28189     {
28190         if (!this.tab) {
28191             return;
28192         }
28193         this.title = str;
28194         this.tab.select('a', true).first().dom.innerHTML = str;
28195         
28196     }
28197     
28198     
28199     
28200 });
28201
28202  
28203
28204
28205  /*
28206  * - LGPL
28207  *
28208  * menu
28209  * 
28210  */
28211 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28212
28213 /**
28214  * @class Roo.bootstrap.menu.Menu
28215  * @extends Roo.bootstrap.Component
28216  * Bootstrap Menu class - container for Menu
28217  * @cfg {String} html Text of the menu
28218  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28219  * @cfg {String} icon Font awesome icon
28220  * @cfg {String} pos Menu align to (top | bottom) default bottom
28221  * 
28222  * 
28223  * @constructor
28224  * Create a new Menu
28225  * @param {Object} config The config object
28226  */
28227
28228
28229 Roo.bootstrap.menu.Menu = function(config){
28230     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28231     
28232     this.addEvents({
28233         /**
28234          * @event beforeshow
28235          * Fires before this menu is displayed
28236          * @param {Roo.bootstrap.menu.Menu} this
28237          */
28238         beforeshow : true,
28239         /**
28240          * @event beforehide
28241          * Fires before this menu is hidden
28242          * @param {Roo.bootstrap.menu.Menu} this
28243          */
28244         beforehide : true,
28245         /**
28246          * @event show
28247          * Fires after this menu is displayed
28248          * @param {Roo.bootstrap.menu.Menu} this
28249          */
28250         show : true,
28251         /**
28252          * @event hide
28253          * Fires after this menu is hidden
28254          * @param {Roo.bootstrap.menu.Menu} this
28255          */
28256         hide : true,
28257         /**
28258          * @event click
28259          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28260          * @param {Roo.bootstrap.menu.Menu} this
28261          * @param {Roo.EventObject} e
28262          */
28263         click : true
28264     });
28265     
28266 };
28267
28268 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28269     
28270     submenu : false,
28271     html : '',
28272     weight : 'default',
28273     icon : false,
28274     pos : 'bottom',
28275     
28276     
28277     getChildContainer : function() {
28278         if(this.isSubMenu){
28279             return this.el;
28280         }
28281         
28282         return this.el.select('ul.dropdown-menu', true).first();  
28283     },
28284     
28285     getAutoCreate : function()
28286     {
28287         var text = [
28288             {
28289                 tag : 'span',
28290                 cls : 'roo-menu-text',
28291                 html : this.html
28292             }
28293         ];
28294         
28295         if(this.icon){
28296             text.unshift({
28297                 tag : 'i',
28298                 cls : 'fa ' + this.icon
28299             })
28300         }
28301         
28302         
28303         var cfg = {
28304             tag : 'div',
28305             cls : 'btn-group',
28306             cn : [
28307                 {
28308                     tag : 'button',
28309                     cls : 'dropdown-button btn btn-' + this.weight,
28310                     cn : text
28311                 },
28312                 {
28313                     tag : 'button',
28314                     cls : 'dropdown-toggle btn btn-' + this.weight,
28315                     cn : [
28316                         {
28317                             tag : 'span',
28318                             cls : 'caret'
28319                         }
28320                     ]
28321                 },
28322                 {
28323                     tag : 'ul',
28324                     cls : 'dropdown-menu'
28325                 }
28326             ]
28327             
28328         };
28329         
28330         if(this.pos == 'top'){
28331             cfg.cls += ' dropup';
28332         }
28333         
28334         if(this.isSubMenu){
28335             cfg = {
28336                 tag : 'ul',
28337                 cls : 'dropdown-menu'
28338             }
28339         }
28340         
28341         return cfg;
28342     },
28343     
28344     onRender : function(ct, position)
28345     {
28346         this.isSubMenu = ct.hasClass('dropdown-submenu');
28347         
28348         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28349     },
28350     
28351     initEvents : function() 
28352     {
28353         if(this.isSubMenu){
28354             return;
28355         }
28356         
28357         this.hidden = true;
28358         
28359         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28360         this.triggerEl.on('click', this.onTriggerPress, this);
28361         
28362         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28363         this.buttonEl.on('click', this.onClick, this);
28364         
28365     },
28366     
28367     list : function()
28368     {
28369         if(this.isSubMenu){
28370             return this.el;
28371         }
28372         
28373         return this.el.select('ul.dropdown-menu', true).first();
28374     },
28375     
28376     onClick : function(e)
28377     {
28378         this.fireEvent("click", this, e);
28379     },
28380     
28381     onTriggerPress  : function(e)
28382     {   
28383         if (this.isVisible()) {
28384             this.hide();
28385         } else {
28386             this.show();
28387         }
28388     },
28389     
28390     isVisible : function(){
28391         return !this.hidden;
28392     },
28393     
28394     show : function()
28395     {
28396         this.fireEvent("beforeshow", this);
28397         
28398         this.hidden = false;
28399         this.el.addClass('open');
28400         
28401         Roo.get(document).on("mouseup", this.onMouseUp, this);
28402         
28403         this.fireEvent("show", this);
28404         
28405         
28406     },
28407     
28408     hide : function()
28409     {
28410         this.fireEvent("beforehide", this);
28411         
28412         this.hidden = true;
28413         this.el.removeClass('open');
28414         
28415         Roo.get(document).un("mouseup", this.onMouseUp);
28416         
28417         this.fireEvent("hide", this);
28418     },
28419     
28420     onMouseUp : function()
28421     {
28422         this.hide();
28423     }
28424     
28425 });
28426
28427  
28428  /*
28429  * - LGPL
28430  *
28431  * menu item
28432  * 
28433  */
28434 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28435
28436 /**
28437  * @class Roo.bootstrap.menu.Item
28438  * @extends Roo.bootstrap.Component
28439  * Bootstrap MenuItem class
28440  * @cfg {Boolean} submenu (true | false) default false
28441  * @cfg {String} html text of the item
28442  * @cfg {String} href the link
28443  * @cfg {Boolean} disable (true | false) default false
28444  * @cfg {Boolean} preventDefault (true | false) default true
28445  * @cfg {String} icon Font awesome icon
28446  * @cfg {String} pos Submenu align to (left | right) default right 
28447  * 
28448  * 
28449  * @constructor
28450  * Create a new Item
28451  * @param {Object} config The config object
28452  */
28453
28454
28455 Roo.bootstrap.menu.Item = function(config){
28456     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28457     this.addEvents({
28458         /**
28459          * @event mouseover
28460          * Fires when the mouse is hovering over this menu
28461          * @param {Roo.bootstrap.menu.Item} this
28462          * @param {Roo.EventObject} e
28463          */
28464         mouseover : true,
28465         /**
28466          * @event mouseout
28467          * Fires when the mouse exits this menu
28468          * @param {Roo.bootstrap.menu.Item} this
28469          * @param {Roo.EventObject} e
28470          */
28471         mouseout : true,
28472         // raw events
28473         /**
28474          * @event click
28475          * The raw click event for the entire grid.
28476          * @param {Roo.EventObject} e
28477          */
28478         click : true
28479     });
28480 };
28481
28482 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28483     
28484     submenu : false,
28485     href : '',
28486     html : '',
28487     preventDefault: true,
28488     disable : false,
28489     icon : false,
28490     pos : 'right',
28491     
28492     getAutoCreate : function()
28493     {
28494         var text = [
28495             {
28496                 tag : 'span',
28497                 cls : 'roo-menu-item-text',
28498                 html : this.html
28499             }
28500         ];
28501         
28502         if(this.icon){
28503             text.unshift({
28504                 tag : 'i',
28505                 cls : 'fa ' + this.icon
28506             })
28507         }
28508         
28509         var cfg = {
28510             tag : 'li',
28511             cn : [
28512                 {
28513                     tag : 'a',
28514                     href : this.href || '#',
28515                     cn : text
28516                 }
28517             ]
28518         };
28519         
28520         if(this.disable){
28521             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28522         }
28523         
28524         if(this.submenu){
28525             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28526             
28527             if(this.pos == 'left'){
28528                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28529             }
28530         }
28531         
28532         return cfg;
28533     },
28534     
28535     initEvents : function() 
28536     {
28537         this.el.on('mouseover', this.onMouseOver, this);
28538         this.el.on('mouseout', this.onMouseOut, this);
28539         
28540         this.el.select('a', true).first().on('click', this.onClick, this);
28541         
28542     },
28543     
28544     onClick : function(e)
28545     {
28546         if(this.preventDefault){
28547             e.preventDefault();
28548         }
28549         
28550         this.fireEvent("click", this, e);
28551     },
28552     
28553     onMouseOver : function(e)
28554     {
28555         if(this.submenu && this.pos == 'left'){
28556             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28557         }
28558         
28559         this.fireEvent("mouseover", this, e);
28560     },
28561     
28562     onMouseOut : function(e)
28563     {
28564         this.fireEvent("mouseout", this, e);
28565     }
28566 });
28567
28568  
28569
28570  /*
28571  * - LGPL
28572  *
28573  * menu separator
28574  * 
28575  */
28576 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28577
28578 /**
28579  * @class Roo.bootstrap.menu.Separator
28580  * @extends Roo.bootstrap.Component
28581  * Bootstrap Separator class
28582  * 
28583  * @constructor
28584  * Create a new Separator
28585  * @param {Object} config The config object
28586  */
28587
28588
28589 Roo.bootstrap.menu.Separator = function(config){
28590     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28591 };
28592
28593 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28594     
28595     getAutoCreate : function(){
28596         var cfg = {
28597             tag : 'li',
28598             cls: 'divider'
28599         };
28600         
28601         return cfg;
28602     }
28603    
28604 });
28605
28606  
28607
28608  /*
28609  * - LGPL
28610  *
28611  * Tooltip
28612  * 
28613  */
28614
28615 /**
28616  * @class Roo.bootstrap.Tooltip
28617  * Bootstrap Tooltip class
28618  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28619  * to determine which dom element triggers the tooltip.
28620  * 
28621  * It needs to add support for additional attributes like tooltip-position
28622  * 
28623  * @constructor
28624  * Create a new Toolti
28625  * @param {Object} config The config object
28626  */
28627
28628 Roo.bootstrap.Tooltip = function(config){
28629     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28630     
28631     this.alignment = Roo.bootstrap.Tooltip.alignment;
28632     
28633     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28634         this.alignment = config.alignment;
28635     }
28636     
28637 };
28638
28639 Roo.apply(Roo.bootstrap.Tooltip, {
28640     /**
28641      * @function init initialize tooltip monitoring.
28642      * @static
28643      */
28644     currentEl : false,
28645     currentTip : false,
28646     currentRegion : false,
28647     
28648     //  init : delay?
28649     
28650     init : function()
28651     {
28652         Roo.get(document).on('mouseover', this.enter ,this);
28653         Roo.get(document).on('mouseout', this.leave, this);
28654          
28655         
28656         this.currentTip = new Roo.bootstrap.Tooltip();
28657     },
28658     
28659     enter : function(ev)
28660     {
28661         var dom = ev.getTarget();
28662         
28663         //Roo.log(['enter',dom]);
28664         var el = Roo.fly(dom);
28665         if (this.currentEl) {
28666             //Roo.log(dom);
28667             //Roo.log(this.currentEl);
28668             //Roo.log(this.currentEl.contains(dom));
28669             if (this.currentEl == el) {
28670                 return;
28671             }
28672             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28673                 return;
28674             }
28675
28676         }
28677         
28678         if (this.currentTip.el) {
28679             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28680         }    
28681         //Roo.log(ev);
28682         
28683         if(!el || el.dom == document){
28684             return;
28685         }
28686         
28687         var bindEl = el;
28688         
28689         // you can not look for children, as if el is the body.. then everythign is the child..
28690         if (!el.attr('tooltip')) { //
28691             if (!el.select("[tooltip]").elements.length) {
28692                 return;
28693             }
28694             // is the mouse over this child...?
28695             bindEl = el.select("[tooltip]").first();
28696             var xy = ev.getXY();
28697             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28698                 //Roo.log("not in region.");
28699                 return;
28700             }
28701             //Roo.log("child element over..");
28702             
28703         }
28704         this.currentEl = bindEl;
28705         this.currentTip.bind(bindEl);
28706         this.currentRegion = Roo.lib.Region.getRegion(dom);
28707         this.currentTip.enter();
28708         
28709     },
28710     leave : function(ev)
28711     {
28712         var dom = ev.getTarget();
28713         //Roo.log(['leave',dom]);
28714         if (!this.currentEl) {
28715             return;
28716         }
28717         
28718         
28719         if (dom != this.currentEl.dom) {
28720             return;
28721         }
28722         var xy = ev.getXY();
28723         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28724             return;
28725         }
28726         // only activate leave if mouse cursor is outside... bounding box..
28727         
28728         
28729         
28730         
28731         if (this.currentTip) {
28732             this.currentTip.leave();
28733         }
28734         //Roo.log('clear currentEl');
28735         this.currentEl = false;
28736         
28737         
28738     },
28739     alignment : {
28740         'left' : ['r-l', [-2,0], 'right'],
28741         'right' : ['l-r', [2,0], 'left'],
28742         'bottom' : ['t-b', [0,2], 'top'],
28743         'top' : [ 'b-t', [0,-2], 'bottom']
28744     }
28745     
28746 });
28747
28748
28749 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28750     
28751     
28752     bindEl : false,
28753     
28754     delay : null, // can be { show : 300 , hide: 500}
28755     
28756     timeout : null,
28757     
28758     hoverState : null, //???
28759     
28760     placement : 'bottom', 
28761     
28762     alignment : false,
28763     
28764     getAutoCreate : function(){
28765     
28766         var cfg = {
28767            cls : 'tooltip',   
28768            role : 'tooltip',
28769            cn : [
28770                 {
28771                     cls : 'tooltip-arrow arrow'
28772                 },
28773                 {
28774                     cls : 'tooltip-inner'
28775                 }
28776            ]
28777         };
28778         
28779         return cfg;
28780     },
28781     bind : function(el)
28782     {
28783         this.bindEl = el;
28784     },
28785     
28786     initEvents : function()
28787     {
28788         this.arrowEl = this.el.select('.arrow', true).first();
28789         this.innerEl = this.el.select('.tooltip-inner', true).first();
28790     },
28791     
28792     enter : function () {
28793        
28794         if (this.timeout != null) {
28795             clearTimeout(this.timeout);
28796         }
28797         
28798         this.hoverState = 'in';
28799          //Roo.log("enter - show");
28800         if (!this.delay || !this.delay.show) {
28801             this.show();
28802             return;
28803         }
28804         var _t = this;
28805         this.timeout = setTimeout(function () {
28806             if (_t.hoverState == 'in') {
28807                 _t.show();
28808             }
28809         }, this.delay.show);
28810     },
28811     leave : function()
28812     {
28813         clearTimeout(this.timeout);
28814     
28815         this.hoverState = 'out';
28816          if (!this.delay || !this.delay.hide) {
28817             this.hide();
28818             return;
28819         }
28820        
28821         var _t = this;
28822         this.timeout = setTimeout(function () {
28823             //Roo.log("leave - timeout");
28824             
28825             if (_t.hoverState == 'out') {
28826                 _t.hide();
28827                 Roo.bootstrap.Tooltip.currentEl = false;
28828             }
28829         }, delay);
28830     },
28831     
28832     show : function (msg)
28833     {
28834         if (!this.el) {
28835             this.render(document.body);
28836         }
28837         // set content.
28838         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28839         
28840         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28841         
28842         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28843         
28844         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28845                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28846         
28847         var placement = typeof this.placement == 'function' ?
28848             this.placement.call(this, this.el, on_el) :
28849             this.placement;
28850             
28851         var autoToken = /\s?auto?\s?/i;
28852         var autoPlace = autoToken.test(placement);
28853         if (autoPlace) {
28854             placement = placement.replace(autoToken, '') || 'top';
28855         }
28856         
28857         //this.el.detach()
28858         //this.el.setXY([0,0]);
28859         this.el.show();
28860         //this.el.dom.style.display='block';
28861         
28862         //this.el.appendTo(on_el);
28863         
28864         var p = this.getPosition();
28865         var box = this.el.getBox();
28866         
28867         if (autoPlace) {
28868             // fixme..
28869         }
28870         
28871         var align = this.alignment[placement];
28872         
28873         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28874         
28875         if(placement == 'top' || placement == 'bottom'){
28876             if(xy[0] < 0){
28877                 placement = 'right';
28878             }
28879             
28880             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28881                 placement = 'left';
28882             }
28883             
28884             var scroll = Roo.select('body', true).first().getScroll();
28885             
28886             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28887                 placement = 'top';
28888             }
28889             
28890             align = this.alignment[placement];
28891             
28892             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28893             
28894         }
28895         
28896         this.el.alignTo(this.bindEl, align[0],align[1]);
28897         //var arrow = this.el.select('.arrow',true).first();
28898         //arrow.set(align[2], 
28899         
28900         this.el.addClass(placement);
28901         this.el.addClass("bs-tooltip-"+ placement);
28902         
28903         this.el.addClass('in fade show');
28904         
28905         this.hoverState = null;
28906         
28907         if (this.el.hasClass('fade')) {
28908             // fade it?
28909         }
28910         
28911         
28912         
28913         
28914         
28915     },
28916     hide : function()
28917     {
28918          
28919         if (!this.el) {
28920             return;
28921         }
28922         //this.el.setXY([0,0]);
28923         this.el.removeClass(['show', 'in']);
28924         //this.el.hide();
28925         
28926     }
28927     
28928 });
28929  
28930
28931  /*
28932  * - LGPL
28933  *
28934  * Location Picker
28935  * 
28936  */
28937
28938 /**
28939  * @class Roo.bootstrap.LocationPicker
28940  * @extends Roo.bootstrap.Component
28941  * Bootstrap LocationPicker class
28942  * @cfg {Number} latitude Position when init default 0
28943  * @cfg {Number} longitude Position when init default 0
28944  * @cfg {Number} zoom default 15
28945  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28946  * @cfg {Boolean} mapTypeControl default false
28947  * @cfg {Boolean} disableDoubleClickZoom default false
28948  * @cfg {Boolean} scrollwheel default true
28949  * @cfg {Boolean} streetViewControl default false
28950  * @cfg {Number} radius default 0
28951  * @cfg {String} locationName
28952  * @cfg {Boolean} draggable default true
28953  * @cfg {Boolean} enableAutocomplete default false
28954  * @cfg {Boolean} enableReverseGeocode default true
28955  * @cfg {String} markerTitle
28956  * 
28957  * @constructor
28958  * Create a new LocationPicker
28959  * @param {Object} config The config object
28960  */
28961
28962
28963 Roo.bootstrap.LocationPicker = function(config){
28964     
28965     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28966     
28967     this.addEvents({
28968         /**
28969          * @event initial
28970          * Fires when the picker initialized.
28971          * @param {Roo.bootstrap.LocationPicker} this
28972          * @param {Google Location} location
28973          */
28974         initial : true,
28975         /**
28976          * @event positionchanged
28977          * Fires when the picker position changed.
28978          * @param {Roo.bootstrap.LocationPicker} this
28979          * @param {Google Location} location
28980          */
28981         positionchanged : true,
28982         /**
28983          * @event resize
28984          * Fires when the map resize.
28985          * @param {Roo.bootstrap.LocationPicker} this
28986          */
28987         resize : true,
28988         /**
28989          * @event show
28990          * Fires when the map show.
28991          * @param {Roo.bootstrap.LocationPicker} this
28992          */
28993         show : true,
28994         /**
28995          * @event hide
28996          * Fires when the map hide.
28997          * @param {Roo.bootstrap.LocationPicker} this
28998          */
28999         hide : true,
29000         /**
29001          * @event mapClick
29002          * Fires when click the map.
29003          * @param {Roo.bootstrap.LocationPicker} this
29004          * @param {Map event} e
29005          */
29006         mapClick : true,
29007         /**
29008          * @event mapRightClick
29009          * Fires when right click the map.
29010          * @param {Roo.bootstrap.LocationPicker} this
29011          * @param {Map event} e
29012          */
29013         mapRightClick : true,
29014         /**
29015          * @event markerClick
29016          * Fires when click the marker.
29017          * @param {Roo.bootstrap.LocationPicker} this
29018          * @param {Map event} e
29019          */
29020         markerClick : true,
29021         /**
29022          * @event markerRightClick
29023          * Fires when right click the marker.
29024          * @param {Roo.bootstrap.LocationPicker} this
29025          * @param {Map event} e
29026          */
29027         markerRightClick : true,
29028         /**
29029          * @event OverlayViewDraw
29030          * Fires when OverlayView Draw
29031          * @param {Roo.bootstrap.LocationPicker} this
29032          */
29033         OverlayViewDraw : true,
29034         /**
29035          * @event OverlayViewOnAdd
29036          * Fires when OverlayView Draw
29037          * @param {Roo.bootstrap.LocationPicker} this
29038          */
29039         OverlayViewOnAdd : true,
29040         /**
29041          * @event OverlayViewOnRemove
29042          * Fires when OverlayView Draw
29043          * @param {Roo.bootstrap.LocationPicker} this
29044          */
29045         OverlayViewOnRemove : true,
29046         /**
29047          * @event OverlayViewShow
29048          * Fires when OverlayView Draw
29049          * @param {Roo.bootstrap.LocationPicker} this
29050          * @param {Pixel} cpx
29051          */
29052         OverlayViewShow : true,
29053         /**
29054          * @event OverlayViewHide
29055          * Fires when OverlayView Draw
29056          * @param {Roo.bootstrap.LocationPicker} this
29057          */
29058         OverlayViewHide : true,
29059         /**
29060          * @event loadexception
29061          * Fires when load google lib failed.
29062          * @param {Roo.bootstrap.LocationPicker} this
29063          */
29064         loadexception : true
29065     });
29066         
29067 };
29068
29069 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29070     
29071     gMapContext: false,
29072     
29073     latitude: 0,
29074     longitude: 0,
29075     zoom: 15,
29076     mapTypeId: false,
29077     mapTypeControl: false,
29078     disableDoubleClickZoom: false,
29079     scrollwheel: true,
29080     streetViewControl: false,
29081     radius: 0,
29082     locationName: '',
29083     draggable: true,
29084     enableAutocomplete: false,
29085     enableReverseGeocode: true,
29086     markerTitle: '',
29087     
29088     getAutoCreate: function()
29089     {
29090
29091         var cfg = {
29092             tag: 'div',
29093             cls: 'roo-location-picker'
29094         };
29095         
29096         return cfg
29097     },
29098     
29099     initEvents: function(ct, position)
29100     {       
29101         if(!this.el.getWidth() || this.isApplied()){
29102             return;
29103         }
29104         
29105         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29106         
29107         this.initial();
29108     },
29109     
29110     initial: function()
29111     {
29112         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29113             this.fireEvent('loadexception', this);
29114             return;
29115         }
29116         
29117         if(!this.mapTypeId){
29118             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29119         }
29120         
29121         this.gMapContext = this.GMapContext();
29122         
29123         this.initOverlayView();
29124         
29125         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29126         
29127         var _this = this;
29128                 
29129         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29130             _this.setPosition(_this.gMapContext.marker.position);
29131         });
29132         
29133         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29134             _this.fireEvent('mapClick', this, event);
29135             
29136         });
29137
29138         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29139             _this.fireEvent('mapRightClick', this, event);
29140             
29141         });
29142         
29143         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29144             _this.fireEvent('markerClick', this, event);
29145             
29146         });
29147
29148         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29149             _this.fireEvent('markerRightClick', this, event);
29150             
29151         });
29152         
29153         this.setPosition(this.gMapContext.location);
29154         
29155         this.fireEvent('initial', this, this.gMapContext.location);
29156     },
29157     
29158     initOverlayView: function()
29159     {
29160         var _this = this;
29161         
29162         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29163             
29164             draw: function()
29165             {
29166                 _this.fireEvent('OverlayViewDraw', _this);
29167             },
29168             
29169             onAdd: function()
29170             {
29171                 _this.fireEvent('OverlayViewOnAdd', _this);
29172             },
29173             
29174             onRemove: function()
29175             {
29176                 _this.fireEvent('OverlayViewOnRemove', _this);
29177             },
29178             
29179             show: function(cpx)
29180             {
29181                 _this.fireEvent('OverlayViewShow', _this, cpx);
29182             },
29183             
29184             hide: function()
29185             {
29186                 _this.fireEvent('OverlayViewHide', _this);
29187             }
29188             
29189         });
29190     },
29191     
29192     fromLatLngToContainerPixel: function(event)
29193     {
29194         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29195     },
29196     
29197     isApplied: function() 
29198     {
29199         return this.getGmapContext() == false ? false : true;
29200     },
29201     
29202     getGmapContext: function() 
29203     {
29204         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29205     },
29206     
29207     GMapContext: function() 
29208     {
29209         var position = new google.maps.LatLng(this.latitude, this.longitude);
29210         
29211         var _map = new google.maps.Map(this.el.dom, {
29212             center: position,
29213             zoom: this.zoom,
29214             mapTypeId: this.mapTypeId,
29215             mapTypeControl: this.mapTypeControl,
29216             disableDoubleClickZoom: this.disableDoubleClickZoom,
29217             scrollwheel: this.scrollwheel,
29218             streetViewControl: this.streetViewControl,
29219             locationName: this.locationName,
29220             draggable: this.draggable,
29221             enableAutocomplete: this.enableAutocomplete,
29222             enableReverseGeocode: this.enableReverseGeocode
29223         });
29224         
29225         var _marker = new google.maps.Marker({
29226             position: position,
29227             map: _map,
29228             title: this.markerTitle,
29229             draggable: this.draggable
29230         });
29231         
29232         return {
29233             map: _map,
29234             marker: _marker,
29235             circle: null,
29236             location: position,
29237             radius: this.radius,
29238             locationName: this.locationName,
29239             addressComponents: {
29240                 formatted_address: null,
29241                 addressLine1: null,
29242                 addressLine2: null,
29243                 streetName: null,
29244                 streetNumber: null,
29245                 city: null,
29246                 district: null,
29247                 state: null,
29248                 stateOrProvince: null
29249             },
29250             settings: this,
29251             domContainer: this.el.dom,
29252             geodecoder: new google.maps.Geocoder()
29253         };
29254     },
29255     
29256     drawCircle: function(center, radius, options) 
29257     {
29258         if (this.gMapContext.circle != null) {
29259             this.gMapContext.circle.setMap(null);
29260         }
29261         if (radius > 0) {
29262             radius *= 1;
29263             options = Roo.apply({}, options, {
29264                 strokeColor: "#0000FF",
29265                 strokeOpacity: .35,
29266                 strokeWeight: 2,
29267                 fillColor: "#0000FF",
29268                 fillOpacity: .2
29269             });
29270             
29271             options.map = this.gMapContext.map;
29272             options.radius = radius;
29273             options.center = center;
29274             this.gMapContext.circle = new google.maps.Circle(options);
29275             return this.gMapContext.circle;
29276         }
29277         
29278         return null;
29279     },
29280     
29281     setPosition: function(location) 
29282     {
29283         this.gMapContext.location = location;
29284         this.gMapContext.marker.setPosition(location);
29285         this.gMapContext.map.panTo(location);
29286         this.drawCircle(location, this.gMapContext.radius, {});
29287         
29288         var _this = this;
29289         
29290         if (this.gMapContext.settings.enableReverseGeocode) {
29291             this.gMapContext.geodecoder.geocode({
29292                 latLng: this.gMapContext.location
29293             }, function(results, status) {
29294                 
29295                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29296                     _this.gMapContext.locationName = results[0].formatted_address;
29297                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29298                     
29299                     _this.fireEvent('positionchanged', this, location);
29300                 }
29301             });
29302             
29303             return;
29304         }
29305         
29306         this.fireEvent('positionchanged', this, location);
29307     },
29308     
29309     resize: function()
29310     {
29311         google.maps.event.trigger(this.gMapContext.map, "resize");
29312         
29313         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29314         
29315         this.fireEvent('resize', this);
29316     },
29317     
29318     setPositionByLatLng: function(latitude, longitude)
29319     {
29320         this.setPosition(new google.maps.LatLng(latitude, longitude));
29321     },
29322     
29323     getCurrentPosition: function() 
29324     {
29325         return {
29326             latitude: this.gMapContext.location.lat(),
29327             longitude: this.gMapContext.location.lng()
29328         };
29329     },
29330     
29331     getAddressName: function() 
29332     {
29333         return this.gMapContext.locationName;
29334     },
29335     
29336     getAddressComponents: function() 
29337     {
29338         return this.gMapContext.addressComponents;
29339     },
29340     
29341     address_component_from_google_geocode: function(address_components) 
29342     {
29343         var result = {};
29344         
29345         for (var i = 0; i < address_components.length; i++) {
29346             var component = address_components[i];
29347             if (component.types.indexOf("postal_code") >= 0) {
29348                 result.postalCode = component.short_name;
29349             } else if (component.types.indexOf("street_number") >= 0) {
29350                 result.streetNumber = component.short_name;
29351             } else if (component.types.indexOf("route") >= 0) {
29352                 result.streetName = component.short_name;
29353             } else if (component.types.indexOf("neighborhood") >= 0) {
29354                 result.city = component.short_name;
29355             } else if (component.types.indexOf("locality") >= 0) {
29356                 result.city = component.short_name;
29357             } else if (component.types.indexOf("sublocality") >= 0) {
29358                 result.district = component.short_name;
29359             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29360                 result.stateOrProvince = component.short_name;
29361             } else if (component.types.indexOf("country") >= 0) {
29362                 result.country = component.short_name;
29363             }
29364         }
29365         
29366         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29367         result.addressLine2 = "";
29368         return result;
29369     },
29370     
29371     setZoomLevel: function(zoom)
29372     {
29373         this.gMapContext.map.setZoom(zoom);
29374     },
29375     
29376     show: function()
29377     {
29378         if(!this.el){
29379             return;
29380         }
29381         
29382         this.el.show();
29383         
29384         this.resize();
29385         
29386         this.fireEvent('show', this);
29387     },
29388     
29389     hide: function()
29390     {
29391         if(!this.el){
29392             return;
29393         }
29394         
29395         this.el.hide();
29396         
29397         this.fireEvent('hide', this);
29398     }
29399     
29400 });
29401
29402 Roo.apply(Roo.bootstrap.LocationPicker, {
29403     
29404     OverlayView : function(map, options)
29405     {
29406         options = options || {};
29407         
29408         this.setMap(map);
29409     }
29410     
29411     
29412 });/**
29413  * @class Roo.bootstrap.Alert
29414  * @extends Roo.bootstrap.Component
29415  * Bootstrap Alert class - shows an alert area box
29416  * eg
29417  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29418   Enter a valid email address
29419 </div>
29420  * @licence LGPL
29421  * @cfg {String} title The title of alert
29422  * @cfg {String} html The content of alert
29423  * @cfg {String} weight (  success | info | warning | danger )
29424  * @cfg {String} faicon font-awesomeicon
29425  * 
29426  * @constructor
29427  * Create a new alert
29428  * @param {Object} config The config object
29429  */
29430
29431
29432 Roo.bootstrap.Alert = function(config){
29433     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29434     
29435 };
29436
29437 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29438     
29439     title: '',
29440     html: '',
29441     weight: false,
29442     faicon: false,
29443     
29444     getAutoCreate : function()
29445     {
29446         
29447         var cfg = {
29448             tag : 'div',
29449             cls : 'alert',
29450             cn : [
29451                 {
29452                     tag : 'i',
29453                     cls : 'roo-alert-icon'
29454                     
29455                 },
29456                 {
29457                     tag : 'b',
29458                     cls : 'roo-alert-title',
29459                     html : this.title
29460                 },
29461                 {
29462                     tag : 'span',
29463                     cls : 'roo-alert-text',
29464                     html : this.html
29465                 }
29466             ]
29467         };
29468         
29469         if(this.faicon){
29470             cfg.cn[0].cls += ' fa ' + this.faicon;
29471         }
29472         
29473         if(this.weight){
29474             cfg.cls += ' alert-' + this.weight;
29475         }
29476         
29477         return cfg;
29478     },
29479     
29480     initEvents: function() 
29481     {
29482         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29483     },
29484     
29485     setTitle : function(str)
29486     {
29487         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29488     },
29489     
29490     setText : function(str)
29491     {
29492         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29493     },
29494     
29495     setWeight : function(weight)
29496     {
29497         if(this.weight){
29498             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29499         }
29500         
29501         this.weight = weight;
29502         
29503         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29504     },
29505     
29506     setIcon : function(icon)
29507     {
29508         if(this.faicon){
29509             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29510         }
29511         
29512         this.faicon = icon;
29513         
29514         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29515     },
29516     
29517     hide: function() 
29518     {
29519         this.el.hide();   
29520     },
29521     
29522     show: function() 
29523     {  
29524         this.el.show();   
29525     }
29526     
29527 });
29528
29529  
29530 /*
29531 * Licence: LGPL
29532 */
29533
29534 /**
29535  * @class Roo.bootstrap.UploadCropbox
29536  * @extends Roo.bootstrap.Component
29537  * Bootstrap UploadCropbox class
29538  * @cfg {String} emptyText show when image has been loaded
29539  * @cfg {String} rotateNotify show when image too small to rotate
29540  * @cfg {Number} errorTimeout default 3000
29541  * @cfg {Number} minWidth default 300
29542  * @cfg {Number} minHeight default 300
29543  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29544  * @cfg {Boolean} isDocument (true|false) default false
29545  * @cfg {String} url action url
29546  * @cfg {String} paramName default 'imageUpload'
29547  * @cfg {String} method default POST
29548  * @cfg {Boolean} loadMask (true|false) default true
29549  * @cfg {Boolean} loadingText default 'Loading...'
29550  * 
29551  * @constructor
29552  * Create a new UploadCropbox
29553  * @param {Object} config The config object
29554  */
29555
29556 Roo.bootstrap.UploadCropbox = function(config){
29557     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29558     
29559     this.addEvents({
29560         /**
29561          * @event beforeselectfile
29562          * Fire before select file
29563          * @param {Roo.bootstrap.UploadCropbox} this
29564          */
29565         "beforeselectfile" : true,
29566         /**
29567          * @event initial
29568          * Fire after initEvent
29569          * @param {Roo.bootstrap.UploadCropbox} this
29570          */
29571         "initial" : true,
29572         /**
29573          * @event crop
29574          * Fire after initEvent
29575          * @param {Roo.bootstrap.UploadCropbox} this
29576          * @param {String} data
29577          */
29578         "crop" : true,
29579         /**
29580          * @event prepare
29581          * Fire when preparing the file data
29582          * @param {Roo.bootstrap.UploadCropbox} this
29583          * @param {Object} file
29584          */
29585         "prepare" : true,
29586         /**
29587          * @event exception
29588          * Fire when get exception
29589          * @param {Roo.bootstrap.UploadCropbox} this
29590          * @param {XMLHttpRequest} xhr
29591          */
29592         "exception" : true,
29593         /**
29594          * @event beforeloadcanvas
29595          * Fire before load the canvas
29596          * @param {Roo.bootstrap.UploadCropbox} this
29597          * @param {String} src
29598          */
29599         "beforeloadcanvas" : true,
29600         /**
29601          * @event trash
29602          * Fire when trash image
29603          * @param {Roo.bootstrap.UploadCropbox} this
29604          */
29605         "trash" : true,
29606         /**
29607          * @event download
29608          * Fire when download the image
29609          * @param {Roo.bootstrap.UploadCropbox} this
29610          */
29611         "download" : true,
29612         /**
29613          * @event footerbuttonclick
29614          * Fire when footerbuttonclick
29615          * @param {Roo.bootstrap.UploadCropbox} this
29616          * @param {String} type
29617          */
29618         "footerbuttonclick" : true,
29619         /**
29620          * @event resize
29621          * Fire when resize
29622          * @param {Roo.bootstrap.UploadCropbox} this
29623          */
29624         "resize" : true,
29625         /**
29626          * @event rotate
29627          * Fire when rotate the image
29628          * @param {Roo.bootstrap.UploadCropbox} this
29629          * @param {String} pos
29630          */
29631         "rotate" : true,
29632         /**
29633          * @event inspect
29634          * Fire when inspect the file
29635          * @param {Roo.bootstrap.UploadCropbox} this
29636          * @param {Object} file
29637          */
29638         "inspect" : true,
29639         /**
29640          * @event upload
29641          * Fire when xhr upload the file
29642          * @param {Roo.bootstrap.UploadCropbox} this
29643          * @param {Object} data
29644          */
29645         "upload" : true,
29646         /**
29647          * @event arrange
29648          * Fire when arrange the file data
29649          * @param {Roo.bootstrap.UploadCropbox} this
29650          * @param {Object} formData
29651          */
29652         "arrange" : true
29653     });
29654     
29655     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29656 };
29657
29658 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29659     
29660     emptyText : 'Click to upload image',
29661     rotateNotify : 'Image is too small to rotate',
29662     errorTimeout : 3000,
29663     scale : 0,
29664     baseScale : 1,
29665     rotate : 0,
29666     dragable : false,
29667     pinching : false,
29668     mouseX : 0,
29669     mouseY : 0,
29670     cropData : false,
29671     minWidth : 300,
29672     minHeight : 300,
29673     file : false,
29674     exif : {},
29675     baseRotate : 1,
29676     cropType : 'image/jpeg',
29677     buttons : false,
29678     canvasLoaded : false,
29679     isDocument : false,
29680     method : 'POST',
29681     paramName : 'imageUpload',
29682     loadMask : true,
29683     loadingText : 'Loading...',
29684     maskEl : false,
29685     
29686     getAutoCreate : function()
29687     {
29688         var cfg = {
29689             tag : 'div',
29690             cls : 'roo-upload-cropbox',
29691             cn : [
29692                 {
29693                     tag : 'input',
29694                     cls : 'roo-upload-cropbox-selector',
29695                     type : 'file'
29696                 },
29697                 {
29698                     tag : 'div',
29699                     cls : 'roo-upload-cropbox-body',
29700                     style : 'cursor:pointer',
29701                     cn : [
29702                         {
29703                             tag : 'div',
29704                             cls : 'roo-upload-cropbox-preview'
29705                         },
29706                         {
29707                             tag : 'div',
29708                             cls : 'roo-upload-cropbox-thumb'
29709                         },
29710                         {
29711                             tag : 'div',
29712                             cls : 'roo-upload-cropbox-empty-notify',
29713                             html : this.emptyText
29714                         },
29715                         {
29716                             tag : 'div',
29717                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29718                             html : this.rotateNotify
29719                         }
29720                     ]
29721                 },
29722                 {
29723                     tag : 'div',
29724                     cls : 'roo-upload-cropbox-footer',
29725                     cn : {
29726                         tag : 'div',
29727                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29728                         cn : []
29729                     }
29730                 }
29731             ]
29732         };
29733         
29734         return cfg;
29735     },
29736     
29737     onRender : function(ct, position)
29738     {
29739         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29740         
29741         if (this.buttons.length) {
29742             
29743             Roo.each(this.buttons, function(bb) {
29744                 
29745                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29746                 
29747                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29748                 
29749             }, this);
29750         }
29751         
29752         if(this.loadMask){
29753             this.maskEl = this.el;
29754         }
29755     },
29756     
29757     initEvents : function()
29758     {
29759         this.urlAPI = (window.createObjectURL && window) || 
29760                                 (window.URL && URL.revokeObjectURL && URL) || 
29761                                 (window.webkitURL && webkitURL);
29762                         
29763         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29764         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29765         
29766         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29767         this.selectorEl.hide();
29768         
29769         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29770         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29771         
29772         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29773         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29774         this.thumbEl.hide();
29775         
29776         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29777         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29778         
29779         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29780         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29781         this.errorEl.hide();
29782         
29783         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29784         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29785         this.footerEl.hide();
29786         
29787         this.setThumbBoxSize();
29788         
29789         this.bind();
29790         
29791         this.resize();
29792         
29793         this.fireEvent('initial', this);
29794     },
29795
29796     bind : function()
29797     {
29798         var _this = this;
29799         
29800         window.addEventListener("resize", function() { _this.resize(); } );
29801         
29802         this.bodyEl.on('click', this.beforeSelectFile, this);
29803         
29804         if(Roo.isTouch){
29805             this.bodyEl.on('touchstart', this.onTouchStart, this);
29806             this.bodyEl.on('touchmove', this.onTouchMove, this);
29807             this.bodyEl.on('touchend', this.onTouchEnd, this);
29808         }
29809         
29810         if(!Roo.isTouch){
29811             this.bodyEl.on('mousedown', this.onMouseDown, this);
29812             this.bodyEl.on('mousemove', this.onMouseMove, this);
29813             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29814             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29815             Roo.get(document).on('mouseup', this.onMouseUp, this);
29816         }
29817         
29818         this.selectorEl.on('change', this.onFileSelected, this);
29819     },
29820     
29821     reset : function()
29822     {    
29823         this.scale = 0;
29824         this.baseScale = 1;
29825         this.rotate = 0;
29826         this.baseRotate = 1;
29827         this.dragable = false;
29828         this.pinching = false;
29829         this.mouseX = 0;
29830         this.mouseY = 0;
29831         this.cropData = false;
29832         this.notifyEl.dom.innerHTML = this.emptyText;
29833         
29834         this.selectorEl.dom.value = '';
29835         
29836     },
29837     
29838     resize : function()
29839     {
29840         if(this.fireEvent('resize', this) != false){
29841             this.setThumbBoxPosition();
29842             this.setCanvasPosition();
29843         }
29844     },
29845     
29846     onFooterButtonClick : function(e, el, o, type)
29847     {
29848         switch (type) {
29849             case 'rotate-left' :
29850                 this.onRotateLeft(e);
29851                 break;
29852             case 'rotate-right' :
29853                 this.onRotateRight(e);
29854                 break;
29855             case 'picture' :
29856                 this.beforeSelectFile(e);
29857                 break;
29858             case 'trash' :
29859                 this.trash(e);
29860                 break;
29861             case 'crop' :
29862                 this.crop(e);
29863                 break;
29864             case 'download' :
29865                 this.download(e);
29866                 break;
29867             default :
29868                 break;
29869         }
29870         
29871         this.fireEvent('footerbuttonclick', this, type);
29872     },
29873     
29874     beforeSelectFile : function(e)
29875     {
29876         e.preventDefault();
29877         
29878         if(this.fireEvent('beforeselectfile', this) != false){
29879             this.selectorEl.dom.click();
29880         }
29881     },
29882     
29883     onFileSelected : function(e)
29884     {
29885         e.preventDefault();
29886         
29887         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29888             return;
29889         }
29890         
29891         var file = this.selectorEl.dom.files[0];
29892         
29893         if(this.fireEvent('inspect', this, file) != false){
29894             this.prepare(file);
29895         }
29896         
29897     },
29898     
29899     trash : function(e)
29900     {
29901         this.fireEvent('trash', this);
29902     },
29903     
29904     download : function(e)
29905     {
29906         this.fireEvent('download', this);
29907     },
29908     
29909     loadCanvas : function(src)
29910     {   
29911         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29912             
29913             this.reset();
29914             
29915             this.imageEl = document.createElement('img');
29916             
29917             var _this = this;
29918             
29919             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29920             
29921             this.imageEl.src = src;
29922         }
29923     },
29924     
29925     onLoadCanvas : function()
29926     {   
29927         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29928         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29929         
29930         this.bodyEl.un('click', this.beforeSelectFile, this);
29931         
29932         this.notifyEl.hide();
29933         this.thumbEl.show();
29934         this.footerEl.show();
29935         
29936         this.baseRotateLevel();
29937         
29938         if(this.isDocument){
29939             this.setThumbBoxSize();
29940         }
29941         
29942         this.setThumbBoxPosition();
29943         
29944         this.baseScaleLevel();
29945         
29946         this.draw();
29947         
29948         this.resize();
29949         
29950         this.canvasLoaded = true;
29951         
29952         if(this.loadMask){
29953             this.maskEl.unmask();
29954         }
29955         
29956     },
29957     
29958     setCanvasPosition : function()
29959     {   
29960         if(!this.canvasEl){
29961             return;
29962         }
29963         
29964         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29965         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29966         
29967         this.previewEl.setLeft(pw);
29968         this.previewEl.setTop(ph);
29969         
29970     },
29971     
29972     onMouseDown : function(e)
29973     {   
29974         e.stopEvent();
29975         
29976         this.dragable = true;
29977         this.pinching = false;
29978         
29979         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29980             this.dragable = false;
29981             return;
29982         }
29983         
29984         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29985         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29986         
29987     },
29988     
29989     onMouseMove : function(e)
29990     {   
29991         e.stopEvent();
29992         
29993         if(!this.canvasLoaded){
29994             return;
29995         }
29996         
29997         if (!this.dragable){
29998             return;
29999         }
30000         
30001         var minX = Math.ceil(this.thumbEl.getLeft(true));
30002         var minY = Math.ceil(this.thumbEl.getTop(true));
30003         
30004         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30005         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30006         
30007         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30008         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30009         
30010         x = x - this.mouseX;
30011         y = y - this.mouseY;
30012         
30013         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30014         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30015         
30016         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30017         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30018         
30019         this.previewEl.setLeft(bgX);
30020         this.previewEl.setTop(bgY);
30021         
30022         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30023         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30024     },
30025     
30026     onMouseUp : function(e)
30027     {   
30028         e.stopEvent();
30029         
30030         this.dragable = false;
30031     },
30032     
30033     onMouseWheel : function(e)
30034     {   
30035         e.stopEvent();
30036         
30037         this.startScale = this.scale;
30038         
30039         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30040         
30041         if(!this.zoomable()){
30042             this.scale = this.startScale;
30043             return;
30044         }
30045         
30046         this.draw();
30047         
30048         return;
30049     },
30050     
30051     zoomable : function()
30052     {
30053         var minScale = this.thumbEl.getWidth() / this.minWidth;
30054         
30055         if(this.minWidth < this.minHeight){
30056             minScale = this.thumbEl.getHeight() / this.minHeight;
30057         }
30058         
30059         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30060         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30061         
30062         if(
30063                 this.isDocument &&
30064                 (this.rotate == 0 || this.rotate == 180) && 
30065                 (
30066                     width > this.imageEl.OriginWidth || 
30067                     height > this.imageEl.OriginHeight ||
30068                     (width < this.minWidth && height < this.minHeight)
30069                 )
30070         ){
30071             return false;
30072         }
30073         
30074         if(
30075                 this.isDocument &&
30076                 (this.rotate == 90 || this.rotate == 270) && 
30077                 (
30078                     width > this.imageEl.OriginWidth || 
30079                     height > this.imageEl.OriginHeight ||
30080                     (width < this.minHeight && height < this.minWidth)
30081                 )
30082         ){
30083             return false;
30084         }
30085         
30086         if(
30087                 !this.isDocument &&
30088                 (this.rotate == 0 || this.rotate == 180) && 
30089                 (
30090                     width < this.minWidth || 
30091                     width > this.imageEl.OriginWidth || 
30092                     height < this.minHeight || 
30093                     height > this.imageEl.OriginHeight
30094                 )
30095         ){
30096             return false;
30097         }
30098         
30099         if(
30100                 !this.isDocument &&
30101                 (this.rotate == 90 || this.rotate == 270) && 
30102                 (
30103                     width < this.minHeight || 
30104                     width > this.imageEl.OriginWidth || 
30105                     height < this.minWidth || 
30106                     height > this.imageEl.OriginHeight
30107                 )
30108         ){
30109             return false;
30110         }
30111         
30112         return true;
30113         
30114     },
30115     
30116     onRotateLeft : function(e)
30117     {   
30118         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30119             
30120             var minScale = this.thumbEl.getWidth() / this.minWidth;
30121             
30122             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30123             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30124             
30125             this.startScale = this.scale;
30126             
30127             while (this.getScaleLevel() < minScale){
30128             
30129                 this.scale = this.scale + 1;
30130                 
30131                 if(!this.zoomable()){
30132                     break;
30133                 }
30134                 
30135                 if(
30136                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30137                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30138                 ){
30139                     continue;
30140                 }
30141                 
30142                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30143
30144                 this.draw();
30145                 
30146                 return;
30147             }
30148             
30149             this.scale = this.startScale;
30150             
30151             this.onRotateFail();
30152             
30153             return false;
30154         }
30155         
30156         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30157
30158         if(this.isDocument){
30159             this.setThumbBoxSize();
30160             this.setThumbBoxPosition();
30161             this.setCanvasPosition();
30162         }
30163         
30164         this.draw();
30165         
30166         this.fireEvent('rotate', this, 'left');
30167         
30168     },
30169     
30170     onRotateRight : function(e)
30171     {
30172         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30173             
30174             var minScale = this.thumbEl.getWidth() / this.minWidth;
30175         
30176             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30177             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30178             
30179             this.startScale = this.scale;
30180             
30181             while (this.getScaleLevel() < minScale){
30182             
30183                 this.scale = this.scale + 1;
30184                 
30185                 if(!this.zoomable()){
30186                     break;
30187                 }
30188                 
30189                 if(
30190                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30191                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30192                 ){
30193                     continue;
30194                 }
30195                 
30196                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30197
30198                 this.draw();
30199                 
30200                 return;
30201             }
30202             
30203             this.scale = this.startScale;
30204             
30205             this.onRotateFail();
30206             
30207             return false;
30208         }
30209         
30210         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30211
30212         if(this.isDocument){
30213             this.setThumbBoxSize();
30214             this.setThumbBoxPosition();
30215             this.setCanvasPosition();
30216         }
30217         
30218         this.draw();
30219         
30220         this.fireEvent('rotate', this, 'right');
30221     },
30222     
30223     onRotateFail : function()
30224     {
30225         this.errorEl.show(true);
30226         
30227         var _this = this;
30228         
30229         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30230     },
30231     
30232     draw : function()
30233     {
30234         this.previewEl.dom.innerHTML = '';
30235         
30236         var canvasEl = document.createElement("canvas");
30237         
30238         var contextEl = canvasEl.getContext("2d");
30239         
30240         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30241         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30242         var center = this.imageEl.OriginWidth / 2;
30243         
30244         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30245             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30246             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30247             center = this.imageEl.OriginHeight / 2;
30248         }
30249         
30250         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30251         
30252         contextEl.translate(center, center);
30253         contextEl.rotate(this.rotate * Math.PI / 180);
30254
30255         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30256         
30257         this.canvasEl = document.createElement("canvas");
30258         
30259         this.contextEl = this.canvasEl.getContext("2d");
30260         
30261         switch (this.rotate) {
30262             case 0 :
30263                 
30264                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30265                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
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 90 : 
30271                 
30272                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30273                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30274                 
30275                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30276                     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);
30277                     break;
30278                 }
30279                 
30280                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30281                 
30282                 break;
30283             case 180 :
30284                 
30285                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30286                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30287                 
30288                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30289                     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);
30290                     break;
30291                 }
30292                 
30293                 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);
30294                 
30295                 break;
30296             case 270 :
30297                 
30298                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30299                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30300         
30301                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30302                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30303                     break;
30304                 }
30305                 
30306                 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);
30307                 
30308                 break;
30309             default : 
30310                 break;
30311         }
30312         
30313         this.previewEl.appendChild(this.canvasEl);
30314         
30315         this.setCanvasPosition();
30316     },
30317     
30318     crop : function()
30319     {
30320         if(!this.canvasLoaded){
30321             return;
30322         }
30323         
30324         var imageCanvas = document.createElement("canvas");
30325         
30326         var imageContext = imageCanvas.getContext("2d");
30327         
30328         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30329         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30330         
30331         var center = imageCanvas.width / 2;
30332         
30333         imageContext.translate(center, center);
30334         
30335         imageContext.rotate(this.rotate * Math.PI / 180);
30336         
30337         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30338         
30339         var canvas = document.createElement("canvas");
30340         
30341         var context = canvas.getContext("2d");
30342                 
30343         canvas.width = this.minWidth;
30344         canvas.height = this.minHeight;
30345
30346         switch (this.rotate) {
30347             case 0 :
30348                 
30349                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30350                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30351                 
30352                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30353                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30354                 
30355                 var targetWidth = this.minWidth - 2 * x;
30356                 var targetHeight = this.minHeight - 2 * y;
30357                 
30358                 var scale = 1;
30359                 
30360                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30361                     scale = targetWidth / width;
30362                 }
30363                 
30364                 if(x > 0 && y == 0){
30365                     scale = targetHeight / height;
30366                 }
30367                 
30368                 if(x > 0 && y > 0){
30369                     scale = targetWidth / width;
30370                     
30371                     if(width < height){
30372                         scale = targetHeight / height;
30373                     }
30374                 }
30375                 
30376                 context.scale(scale, scale);
30377                 
30378                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30379                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30380
30381                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30382                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30383
30384                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30385                 
30386                 break;
30387             case 90 : 
30388                 
30389                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30390                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30391                 
30392                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30393                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30394                 
30395                 var targetWidth = this.minWidth - 2 * x;
30396                 var targetHeight = this.minHeight - 2 * y;
30397                 
30398                 var scale = 1;
30399                 
30400                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30401                     scale = targetWidth / width;
30402                 }
30403                 
30404                 if(x > 0 && y == 0){
30405                     scale = targetHeight / height;
30406                 }
30407                 
30408                 if(x > 0 && y > 0){
30409                     scale = targetWidth / width;
30410                     
30411                     if(width < height){
30412                         scale = targetHeight / height;
30413                     }
30414                 }
30415                 
30416                 context.scale(scale, scale);
30417                 
30418                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30419                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30420
30421                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30422                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30423                 
30424                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30425                 
30426                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30427                 
30428                 break;
30429             case 180 :
30430                 
30431                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30432                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30433                 
30434                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30435                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30436                 
30437                 var targetWidth = this.minWidth - 2 * x;
30438                 var targetHeight = this.minHeight - 2 * y;
30439                 
30440                 var scale = 1;
30441                 
30442                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30443                     scale = targetWidth / width;
30444                 }
30445                 
30446                 if(x > 0 && y == 0){
30447                     scale = targetHeight / height;
30448                 }
30449                 
30450                 if(x > 0 && y > 0){
30451                     scale = targetWidth / width;
30452                     
30453                     if(width < height){
30454                         scale = targetHeight / height;
30455                     }
30456                 }
30457                 
30458                 context.scale(scale, scale);
30459                 
30460                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30461                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30462
30463                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30464                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30465
30466                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30467                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30468                 
30469                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30470                 
30471                 break;
30472             case 270 :
30473                 
30474                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30475                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30476                 
30477                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30478                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30479                 
30480                 var targetWidth = this.minWidth - 2 * x;
30481                 var targetHeight = this.minHeight - 2 * y;
30482                 
30483                 var scale = 1;
30484                 
30485                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30486                     scale = targetWidth / width;
30487                 }
30488                 
30489                 if(x > 0 && y == 0){
30490                     scale = targetHeight / height;
30491                 }
30492                 
30493                 if(x > 0 && y > 0){
30494                     scale = targetWidth / width;
30495                     
30496                     if(width < height){
30497                         scale = targetHeight / height;
30498                     }
30499                 }
30500                 
30501                 context.scale(scale, scale);
30502                 
30503                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30504                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30505
30506                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30507                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30508                 
30509                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30510                 
30511                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30512                 
30513                 break;
30514             default : 
30515                 break;
30516         }
30517         
30518         this.cropData = canvas.toDataURL(this.cropType);
30519         
30520         if(this.fireEvent('crop', this, this.cropData) !== false){
30521             this.process(this.file, this.cropData);
30522         }
30523         
30524         return;
30525         
30526     },
30527     
30528     setThumbBoxSize : function()
30529     {
30530         var width, height;
30531         
30532         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30533             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30534             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30535             
30536             this.minWidth = width;
30537             this.minHeight = height;
30538             
30539             if(this.rotate == 90 || this.rotate == 270){
30540                 this.minWidth = height;
30541                 this.minHeight = width;
30542             }
30543         }
30544         
30545         height = 300;
30546         width = Math.ceil(this.minWidth * height / this.minHeight);
30547         
30548         if(this.minWidth > this.minHeight){
30549             width = 300;
30550             height = Math.ceil(this.minHeight * width / this.minWidth);
30551         }
30552         
30553         this.thumbEl.setStyle({
30554             width : width + 'px',
30555             height : height + 'px'
30556         });
30557
30558         return;
30559             
30560     },
30561     
30562     setThumbBoxPosition : function()
30563     {
30564         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30565         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30566         
30567         this.thumbEl.setLeft(x);
30568         this.thumbEl.setTop(y);
30569         
30570     },
30571     
30572     baseRotateLevel : function()
30573     {
30574         this.baseRotate = 1;
30575         
30576         if(
30577                 typeof(this.exif) != 'undefined' &&
30578                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30579                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30580         ){
30581             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30582         }
30583         
30584         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30585         
30586     },
30587     
30588     baseScaleLevel : function()
30589     {
30590         var width, height;
30591         
30592         if(this.isDocument){
30593             
30594             if(this.baseRotate == 6 || this.baseRotate == 8){
30595             
30596                 height = this.thumbEl.getHeight();
30597                 this.baseScale = height / this.imageEl.OriginWidth;
30598
30599                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30600                     width = this.thumbEl.getWidth();
30601                     this.baseScale = width / this.imageEl.OriginHeight;
30602                 }
30603
30604                 return;
30605             }
30606
30607             height = this.thumbEl.getHeight();
30608             this.baseScale = height / this.imageEl.OriginHeight;
30609
30610             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30611                 width = this.thumbEl.getWidth();
30612                 this.baseScale = width / this.imageEl.OriginWidth;
30613             }
30614
30615             return;
30616         }
30617         
30618         if(this.baseRotate == 6 || this.baseRotate == 8){
30619             
30620             width = this.thumbEl.getHeight();
30621             this.baseScale = width / this.imageEl.OriginHeight;
30622             
30623             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30624                 height = this.thumbEl.getWidth();
30625                 this.baseScale = height / this.imageEl.OriginHeight;
30626             }
30627             
30628             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30629                 height = this.thumbEl.getWidth();
30630                 this.baseScale = height / this.imageEl.OriginHeight;
30631                 
30632                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30633                     width = this.thumbEl.getHeight();
30634                     this.baseScale = width / this.imageEl.OriginWidth;
30635                 }
30636             }
30637             
30638             return;
30639         }
30640         
30641         width = this.thumbEl.getWidth();
30642         this.baseScale = width / this.imageEl.OriginWidth;
30643         
30644         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30645             height = this.thumbEl.getHeight();
30646             this.baseScale = height / this.imageEl.OriginHeight;
30647         }
30648         
30649         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30650             
30651             height = this.thumbEl.getHeight();
30652             this.baseScale = height / this.imageEl.OriginHeight;
30653             
30654             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30655                 width = this.thumbEl.getWidth();
30656                 this.baseScale = width / this.imageEl.OriginWidth;
30657             }
30658             
30659         }
30660         
30661         return;
30662     },
30663     
30664     getScaleLevel : function()
30665     {
30666         return this.baseScale * Math.pow(1.1, this.scale);
30667     },
30668     
30669     onTouchStart : function(e)
30670     {
30671         if(!this.canvasLoaded){
30672             this.beforeSelectFile(e);
30673             return;
30674         }
30675         
30676         var touches = e.browserEvent.touches;
30677         
30678         if(!touches){
30679             return;
30680         }
30681         
30682         if(touches.length == 1){
30683             this.onMouseDown(e);
30684             return;
30685         }
30686         
30687         if(touches.length != 2){
30688             return;
30689         }
30690         
30691         var coords = [];
30692         
30693         for(var i = 0, finger; finger = touches[i]; i++){
30694             coords.push(finger.pageX, finger.pageY);
30695         }
30696         
30697         var x = Math.pow(coords[0] - coords[2], 2);
30698         var y = Math.pow(coords[1] - coords[3], 2);
30699         
30700         this.startDistance = Math.sqrt(x + y);
30701         
30702         this.startScale = this.scale;
30703         
30704         this.pinching = true;
30705         this.dragable = false;
30706         
30707     },
30708     
30709     onTouchMove : function(e)
30710     {
30711         if(!this.pinching && !this.dragable){
30712             return;
30713         }
30714         
30715         var touches = e.browserEvent.touches;
30716         
30717         if(!touches){
30718             return;
30719         }
30720         
30721         if(this.dragable){
30722             this.onMouseMove(e);
30723             return;
30724         }
30725         
30726         var coords = [];
30727         
30728         for(var i = 0, finger; finger = touches[i]; i++){
30729             coords.push(finger.pageX, finger.pageY);
30730         }
30731         
30732         var x = Math.pow(coords[0] - coords[2], 2);
30733         var y = Math.pow(coords[1] - coords[3], 2);
30734         
30735         this.endDistance = Math.sqrt(x + y);
30736         
30737         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30738         
30739         if(!this.zoomable()){
30740             this.scale = this.startScale;
30741             return;
30742         }
30743         
30744         this.draw();
30745         
30746     },
30747     
30748     onTouchEnd : function(e)
30749     {
30750         this.pinching = false;
30751         this.dragable = false;
30752         
30753     },
30754     
30755     process : function(file, crop)
30756     {
30757         if(this.loadMask){
30758             this.maskEl.mask(this.loadingText);
30759         }
30760         
30761         this.xhr = new XMLHttpRequest();
30762         
30763         file.xhr = this.xhr;
30764
30765         this.xhr.open(this.method, this.url, true);
30766         
30767         var headers = {
30768             "Accept": "application/json",
30769             "Cache-Control": "no-cache",
30770             "X-Requested-With": "XMLHttpRequest"
30771         };
30772         
30773         for (var headerName in headers) {
30774             var headerValue = headers[headerName];
30775             if (headerValue) {
30776                 this.xhr.setRequestHeader(headerName, headerValue);
30777             }
30778         }
30779         
30780         var _this = this;
30781         
30782         this.xhr.onload = function()
30783         {
30784             _this.xhrOnLoad(_this.xhr);
30785         }
30786         
30787         this.xhr.onerror = function()
30788         {
30789             _this.xhrOnError(_this.xhr);
30790         }
30791         
30792         var formData = new FormData();
30793
30794         formData.append('returnHTML', 'NO');
30795         
30796         if(crop){
30797             formData.append('crop', crop);
30798         }
30799         
30800         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30801             formData.append(this.paramName, file, file.name);
30802         }
30803         
30804         if(typeof(file.filename) != 'undefined'){
30805             formData.append('filename', file.filename);
30806         }
30807         
30808         if(typeof(file.mimetype) != 'undefined'){
30809             formData.append('mimetype', file.mimetype);
30810         }
30811         
30812         if(this.fireEvent('arrange', this, formData) != false){
30813             this.xhr.send(formData);
30814         };
30815     },
30816     
30817     xhrOnLoad : function(xhr)
30818     {
30819         if(this.loadMask){
30820             this.maskEl.unmask();
30821         }
30822         
30823         if (xhr.readyState !== 4) {
30824             this.fireEvent('exception', this, xhr);
30825             return;
30826         }
30827
30828         var response = Roo.decode(xhr.responseText);
30829         
30830         if(!response.success){
30831             this.fireEvent('exception', this, xhr);
30832             return;
30833         }
30834         
30835         var response = Roo.decode(xhr.responseText);
30836         
30837         this.fireEvent('upload', this, response);
30838         
30839     },
30840     
30841     xhrOnError : function()
30842     {
30843         if(this.loadMask){
30844             this.maskEl.unmask();
30845         }
30846         
30847         Roo.log('xhr on error');
30848         
30849         var response = Roo.decode(xhr.responseText);
30850           
30851         Roo.log(response);
30852         
30853     },
30854     
30855     prepare : function(file)
30856     {   
30857         if(this.loadMask){
30858             this.maskEl.mask(this.loadingText);
30859         }
30860         
30861         this.file = false;
30862         this.exif = {};
30863         
30864         if(typeof(file) === 'string'){
30865             this.loadCanvas(file);
30866             return;
30867         }
30868         
30869         if(!file || !this.urlAPI){
30870             return;
30871         }
30872         
30873         this.file = file;
30874         this.cropType = file.type;
30875         
30876         var _this = this;
30877         
30878         if(this.fireEvent('prepare', this, this.file) != false){
30879             
30880             var reader = new FileReader();
30881             
30882             reader.onload = function (e) {
30883                 if (e.target.error) {
30884                     Roo.log(e.target.error);
30885                     return;
30886                 }
30887                 
30888                 var buffer = e.target.result,
30889                     dataView = new DataView(buffer),
30890                     offset = 2,
30891                     maxOffset = dataView.byteLength - 4,
30892                     markerBytes,
30893                     markerLength;
30894                 
30895                 if (dataView.getUint16(0) === 0xffd8) {
30896                     while (offset < maxOffset) {
30897                         markerBytes = dataView.getUint16(offset);
30898                         
30899                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30900                             markerLength = dataView.getUint16(offset + 2) + 2;
30901                             if (offset + markerLength > dataView.byteLength) {
30902                                 Roo.log('Invalid meta data: Invalid segment size.');
30903                                 break;
30904                             }
30905                             
30906                             if(markerBytes == 0xffe1){
30907                                 _this.parseExifData(
30908                                     dataView,
30909                                     offset,
30910                                     markerLength
30911                                 );
30912                             }
30913                             
30914                             offset += markerLength;
30915                             
30916                             continue;
30917                         }
30918                         
30919                         break;
30920                     }
30921                     
30922                 }
30923                 
30924                 var url = _this.urlAPI.createObjectURL(_this.file);
30925                 
30926                 _this.loadCanvas(url);
30927                 
30928                 return;
30929             }
30930             
30931             reader.readAsArrayBuffer(this.file);
30932             
30933         }
30934         
30935     },
30936     
30937     parseExifData : function(dataView, offset, length)
30938     {
30939         var tiffOffset = offset + 10,
30940             littleEndian,
30941             dirOffset;
30942     
30943         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30944             // No Exif data, might be XMP data instead
30945             return;
30946         }
30947         
30948         // Check for the ASCII code for "Exif" (0x45786966):
30949         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30950             // No Exif data, might be XMP data instead
30951             return;
30952         }
30953         if (tiffOffset + 8 > dataView.byteLength) {
30954             Roo.log('Invalid Exif data: Invalid segment size.');
30955             return;
30956         }
30957         // Check for the two null bytes:
30958         if (dataView.getUint16(offset + 8) !== 0x0000) {
30959             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30960             return;
30961         }
30962         // Check the byte alignment:
30963         switch (dataView.getUint16(tiffOffset)) {
30964         case 0x4949:
30965             littleEndian = true;
30966             break;
30967         case 0x4D4D:
30968             littleEndian = false;
30969             break;
30970         default:
30971             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30972             return;
30973         }
30974         // Check for the TIFF tag marker (0x002A):
30975         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30976             Roo.log('Invalid Exif data: Missing TIFF marker.');
30977             return;
30978         }
30979         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30980         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30981         
30982         this.parseExifTags(
30983             dataView,
30984             tiffOffset,
30985             tiffOffset + dirOffset,
30986             littleEndian
30987         );
30988     },
30989     
30990     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30991     {
30992         var tagsNumber,
30993             dirEndOffset,
30994             i;
30995         if (dirOffset + 6 > dataView.byteLength) {
30996             Roo.log('Invalid Exif data: Invalid directory offset.');
30997             return;
30998         }
30999         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31000         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31001         if (dirEndOffset + 4 > dataView.byteLength) {
31002             Roo.log('Invalid Exif data: Invalid directory size.');
31003             return;
31004         }
31005         for (i = 0; i < tagsNumber; i += 1) {
31006             this.parseExifTag(
31007                 dataView,
31008                 tiffOffset,
31009                 dirOffset + 2 + 12 * i, // tag offset
31010                 littleEndian
31011             );
31012         }
31013         // Return the offset to the next directory:
31014         return dataView.getUint32(dirEndOffset, littleEndian);
31015     },
31016     
31017     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31018     {
31019         var tag = dataView.getUint16(offset, littleEndian);
31020         
31021         this.exif[tag] = this.getExifValue(
31022             dataView,
31023             tiffOffset,
31024             offset,
31025             dataView.getUint16(offset + 2, littleEndian), // tag type
31026             dataView.getUint32(offset + 4, littleEndian), // tag length
31027             littleEndian
31028         );
31029     },
31030     
31031     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31032     {
31033         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31034             tagSize,
31035             dataOffset,
31036             values,
31037             i,
31038             str,
31039             c;
31040     
31041         if (!tagType) {
31042             Roo.log('Invalid Exif data: Invalid tag type.');
31043             return;
31044         }
31045         
31046         tagSize = tagType.size * length;
31047         // Determine if the value is contained in the dataOffset bytes,
31048         // or if the value at the dataOffset is a pointer to the actual data:
31049         dataOffset = tagSize > 4 ?
31050                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31051         if (dataOffset + tagSize > dataView.byteLength) {
31052             Roo.log('Invalid Exif data: Invalid data offset.');
31053             return;
31054         }
31055         if (length === 1) {
31056             return tagType.getValue(dataView, dataOffset, littleEndian);
31057         }
31058         values = [];
31059         for (i = 0; i < length; i += 1) {
31060             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31061         }
31062         
31063         if (tagType.ascii) {
31064             str = '';
31065             // Concatenate the chars:
31066             for (i = 0; i < values.length; i += 1) {
31067                 c = values[i];
31068                 // Ignore the terminating NULL byte(s):
31069                 if (c === '\u0000') {
31070                     break;
31071                 }
31072                 str += c;
31073             }
31074             return str;
31075         }
31076         return values;
31077     }
31078     
31079 });
31080
31081 Roo.apply(Roo.bootstrap.UploadCropbox, {
31082     tags : {
31083         'Orientation': 0x0112
31084     },
31085     
31086     Orientation: {
31087             1: 0, //'top-left',
31088 //            2: 'top-right',
31089             3: 180, //'bottom-right',
31090 //            4: 'bottom-left',
31091 //            5: 'left-top',
31092             6: 90, //'right-top',
31093 //            7: 'right-bottom',
31094             8: 270 //'left-bottom'
31095     },
31096     
31097     exifTagTypes : {
31098         // byte, 8-bit unsigned int:
31099         1: {
31100             getValue: function (dataView, dataOffset) {
31101                 return dataView.getUint8(dataOffset);
31102             },
31103             size: 1
31104         },
31105         // ascii, 8-bit byte:
31106         2: {
31107             getValue: function (dataView, dataOffset) {
31108                 return String.fromCharCode(dataView.getUint8(dataOffset));
31109             },
31110             size: 1,
31111             ascii: true
31112         },
31113         // short, 16 bit int:
31114         3: {
31115             getValue: function (dataView, dataOffset, littleEndian) {
31116                 return dataView.getUint16(dataOffset, littleEndian);
31117             },
31118             size: 2
31119         },
31120         // long, 32 bit int:
31121         4: {
31122             getValue: function (dataView, dataOffset, littleEndian) {
31123                 return dataView.getUint32(dataOffset, littleEndian);
31124             },
31125             size: 4
31126         },
31127         // rational = two long values, first is numerator, second is denominator:
31128         5: {
31129             getValue: function (dataView, dataOffset, littleEndian) {
31130                 return dataView.getUint32(dataOffset, littleEndian) /
31131                     dataView.getUint32(dataOffset + 4, littleEndian);
31132             },
31133             size: 8
31134         },
31135         // slong, 32 bit signed int:
31136         9: {
31137             getValue: function (dataView, dataOffset, littleEndian) {
31138                 return dataView.getInt32(dataOffset, littleEndian);
31139             },
31140             size: 4
31141         },
31142         // srational, two slongs, first is numerator, second is denominator:
31143         10: {
31144             getValue: function (dataView, dataOffset, littleEndian) {
31145                 return dataView.getInt32(dataOffset, littleEndian) /
31146                     dataView.getInt32(dataOffset + 4, littleEndian);
31147             },
31148             size: 8
31149         }
31150     },
31151     
31152     footer : {
31153         STANDARD : [
31154             {
31155                 tag : 'div',
31156                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31157                 action : 'rotate-left',
31158                 cn : [
31159                     {
31160                         tag : 'button',
31161                         cls : 'btn btn-default',
31162                         html : '<i class="fa fa-undo"></i>'
31163                     }
31164                 ]
31165             },
31166             {
31167                 tag : 'div',
31168                 cls : 'btn-group roo-upload-cropbox-picture',
31169                 action : 'picture',
31170                 cn : [
31171                     {
31172                         tag : 'button',
31173                         cls : 'btn btn-default',
31174                         html : '<i class="fa fa-picture-o"></i>'
31175                     }
31176                 ]
31177             },
31178             {
31179                 tag : 'div',
31180                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31181                 action : 'rotate-right',
31182                 cn : [
31183                     {
31184                         tag : 'button',
31185                         cls : 'btn btn-default',
31186                         html : '<i class="fa fa-repeat"></i>'
31187                     }
31188                 ]
31189             }
31190         ],
31191         DOCUMENT : [
31192             {
31193                 tag : 'div',
31194                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31195                 action : 'rotate-left',
31196                 cn : [
31197                     {
31198                         tag : 'button',
31199                         cls : 'btn btn-default',
31200                         html : '<i class="fa fa-undo"></i>'
31201                     }
31202                 ]
31203             },
31204             {
31205                 tag : 'div',
31206                 cls : 'btn-group roo-upload-cropbox-download',
31207                 action : 'download',
31208                 cn : [
31209                     {
31210                         tag : 'button',
31211                         cls : 'btn btn-default',
31212                         html : '<i class="fa fa-download"></i>'
31213                     }
31214                 ]
31215             },
31216             {
31217                 tag : 'div',
31218                 cls : 'btn-group roo-upload-cropbox-crop',
31219                 action : 'crop',
31220                 cn : [
31221                     {
31222                         tag : 'button',
31223                         cls : 'btn btn-default',
31224                         html : '<i class="fa fa-crop"></i>'
31225                     }
31226                 ]
31227             },
31228             {
31229                 tag : 'div',
31230                 cls : 'btn-group roo-upload-cropbox-trash',
31231                 action : 'trash',
31232                 cn : [
31233                     {
31234                         tag : 'button',
31235                         cls : 'btn btn-default',
31236                         html : '<i class="fa fa-trash"></i>'
31237                     }
31238                 ]
31239             },
31240             {
31241                 tag : 'div',
31242                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31243                 action : 'rotate-right',
31244                 cn : [
31245                     {
31246                         tag : 'button',
31247                         cls : 'btn btn-default',
31248                         html : '<i class="fa fa-repeat"></i>'
31249                     }
31250                 ]
31251             }
31252         ],
31253         ROTATOR : [
31254             {
31255                 tag : 'div',
31256                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31257                 action : 'rotate-left',
31258                 cn : [
31259                     {
31260                         tag : 'button',
31261                         cls : 'btn btn-default',
31262                         html : '<i class="fa fa-undo"></i>'
31263                     }
31264                 ]
31265             },
31266             {
31267                 tag : 'div',
31268                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31269                 action : 'rotate-right',
31270                 cn : [
31271                     {
31272                         tag : 'button',
31273                         cls : 'btn btn-default',
31274                         html : '<i class="fa fa-repeat"></i>'
31275                     }
31276                 ]
31277             }
31278         ]
31279     }
31280 });
31281
31282 /*
31283 * Licence: LGPL
31284 */
31285
31286 /**
31287  * @class Roo.bootstrap.DocumentManager
31288  * @extends Roo.bootstrap.Component
31289  * Bootstrap DocumentManager class
31290  * @cfg {String} paramName default 'imageUpload'
31291  * @cfg {String} toolTipName default 'filename'
31292  * @cfg {String} method default POST
31293  * @cfg {String} url action url
31294  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31295  * @cfg {Boolean} multiple multiple upload default true
31296  * @cfg {Number} thumbSize default 300
31297  * @cfg {String} fieldLabel
31298  * @cfg {Number} labelWidth default 4
31299  * @cfg {String} labelAlign (left|top) default left
31300  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31301 * @cfg {Number} labellg set the width of label (1-12)
31302  * @cfg {Number} labelmd set the width of label (1-12)
31303  * @cfg {Number} labelsm set the width of label (1-12)
31304  * @cfg {Number} labelxs set the width of label (1-12)
31305  * 
31306  * @constructor
31307  * Create a new DocumentManager
31308  * @param {Object} config The config object
31309  */
31310
31311 Roo.bootstrap.DocumentManager = function(config){
31312     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31313     
31314     this.files = [];
31315     this.delegates = [];
31316     
31317     this.addEvents({
31318         /**
31319          * @event initial
31320          * Fire when initial the DocumentManager
31321          * @param {Roo.bootstrap.DocumentManager} this
31322          */
31323         "initial" : true,
31324         /**
31325          * @event inspect
31326          * inspect selected file
31327          * @param {Roo.bootstrap.DocumentManager} this
31328          * @param {File} file
31329          */
31330         "inspect" : true,
31331         /**
31332          * @event exception
31333          * Fire when xhr load exception
31334          * @param {Roo.bootstrap.DocumentManager} this
31335          * @param {XMLHttpRequest} xhr
31336          */
31337         "exception" : true,
31338         /**
31339          * @event afterupload
31340          * Fire when xhr load exception
31341          * @param {Roo.bootstrap.DocumentManager} this
31342          * @param {XMLHttpRequest} xhr
31343          */
31344         "afterupload" : true,
31345         /**
31346          * @event prepare
31347          * prepare the form data
31348          * @param {Roo.bootstrap.DocumentManager} this
31349          * @param {Object} formData
31350          */
31351         "prepare" : true,
31352         /**
31353          * @event remove
31354          * Fire when remove the file
31355          * @param {Roo.bootstrap.DocumentManager} this
31356          * @param {Object} file
31357          */
31358         "remove" : true,
31359         /**
31360          * @event refresh
31361          * Fire after refresh the file
31362          * @param {Roo.bootstrap.DocumentManager} this
31363          */
31364         "refresh" : true,
31365         /**
31366          * @event click
31367          * Fire after click the image
31368          * @param {Roo.bootstrap.DocumentManager} this
31369          * @param {Object} file
31370          */
31371         "click" : true,
31372         /**
31373          * @event edit
31374          * Fire when upload a image and editable set to true
31375          * @param {Roo.bootstrap.DocumentManager} this
31376          * @param {Object} file
31377          */
31378         "edit" : true,
31379         /**
31380          * @event beforeselectfile
31381          * Fire before select file
31382          * @param {Roo.bootstrap.DocumentManager} this
31383          */
31384         "beforeselectfile" : true,
31385         /**
31386          * @event process
31387          * Fire before process file
31388          * @param {Roo.bootstrap.DocumentManager} this
31389          * @param {Object} file
31390          */
31391         "process" : true,
31392         /**
31393          * @event previewrendered
31394          * Fire when preview rendered
31395          * @param {Roo.bootstrap.DocumentManager} this
31396          * @param {Object} file
31397          */
31398         "previewrendered" : true,
31399         /**
31400          */
31401         "previewResize" : true
31402         
31403     });
31404 };
31405
31406 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31407     
31408     boxes : 0,
31409     inputName : '',
31410     thumbSize : 300,
31411     multiple : true,
31412     files : false,
31413     method : 'POST',
31414     url : '',
31415     paramName : 'imageUpload',
31416     toolTipName : 'filename',
31417     fieldLabel : '',
31418     labelWidth : 4,
31419     labelAlign : 'left',
31420     editable : true,
31421     delegates : false,
31422     xhr : false, 
31423     
31424     labellg : 0,
31425     labelmd : 0,
31426     labelsm : 0,
31427     labelxs : 0,
31428     
31429     getAutoCreate : function()
31430     {   
31431         var managerWidget = {
31432             tag : 'div',
31433             cls : 'roo-document-manager',
31434             cn : [
31435                 {
31436                     tag : 'input',
31437                     cls : 'roo-document-manager-selector',
31438                     type : 'file'
31439                 },
31440                 {
31441                     tag : 'div',
31442                     cls : 'roo-document-manager-uploader',
31443                     cn : [
31444                         {
31445                             tag : 'div',
31446                             cls : 'roo-document-manager-upload-btn',
31447                             html : '<i class="fa fa-plus"></i>'
31448                         }
31449                     ]
31450                     
31451                 }
31452             ]
31453         };
31454         
31455         var content = [
31456             {
31457                 tag : 'div',
31458                 cls : 'column col-md-12',
31459                 cn : managerWidget
31460             }
31461         ];
31462         
31463         if(this.fieldLabel.length){
31464             
31465             content = [
31466                 {
31467                     tag : 'div',
31468                     cls : 'column col-md-12',
31469                     html : this.fieldLabel
31470                 },
31471                 {
31472                     tag : 'div',
31473                     cls : 'column col-md-12',
31474                     cn : managerWidget
31475                 }
31476             ];
31477
31478             if(this.labelAlign == 'left'){
31479                 content = [
31480                     {
31481                         tag : 'div',
31482                         cls : 'column',
31483                         html : this.fieldLabel
31484                     },
31485                     {
31486                         tag : 'div',
31487                         cls : 'column',
31488                         cn : managerWidget
31489                     }
31490                 ];
31491                 
31492                 if(this.labelWidth > 12){
31493                     content[0].style = "width: " + this.labelWidth + 'px';
31494                 }
31495
31496                 if(this.labelWidth < 13 && this.labelmd == 0){
31497                     this.labelmd = this.labelWidth;
31498                 }
31499
31500                 if(this.labellg > 0){
31501                     content[0].cls += ' col-lg-' + this.labellg;
31502                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31503                 }
31504
31505                 if(this.labelmd > 0){
31506                     content[0].cls += ' col-md-' + this.labelmd;
31507                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31508                 }
31509
31510                 if(this.labelsm > 0){
31511                     content[0].cls += ' col-sm-' + this.labelsm;
31512                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31513                 }
31514
31515                 if(this.labelxs > 0){
31516                     content[0].cls += ' col-xs-' + this.labelxs;
31517                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31518                 }
31519                 
31520             }
31521         }
31522         
31523         var cfg = {
31524             tag : 'div',
31525             cls : 'row clearfix',
31526             cn : content
31527         };
31528         
31529         return cfg;
31530         
31531     },
31532     
31533     initEvents : function()
31534     {
31535         this.managerEl = this.el.select('.roo-document-manager', true).first();
31536         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31537         
31538         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31539         this.selectorEl.hide();
31540         
31541         if(this.multiple){
31542             this.selectorEl.attr('multiple', 'multiple');
31543         }
31544         
31545         this.selectorEl.on('change', this.onFileSelected, this);
31546         
31547         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31548         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31549         
31550         this.uploader.on('click', this.onUploaderClick, this);
31551         
31552         this.renderProgressDialog();
31553         
31554         var _this = this;
31555         
31556         window.addEventListener("resize", function() { _this.refresh(); } );
31557         
31558         this.fireEvent('initial', this);
31559     },
31560     
31561     renderProgressDialog : function()
31562     {
31563         var _this = this;
31564         
31565         this.progressDialog = new Roo.bootstrap.Modal({
31566             cls : 'roo-document-manager-progress-dialog',
31567             allow_close : false,
31568             animate : false,
31569             title : '',
31570             buttons : [
31571                 {
31572                     name  :'cancel',
31573                     weight : 'danger',
31574                     html : 'Cancel'
31575                 }
31576             ], 
31577             listeners : { 
31578                 btnclick : function() {
31579                     _this.uploadCancel();
31580                     this.hide();
31581                 }
31582             }
31583         });
31584          
31585         this.progressDialog.render(Roo.get(document.body));
31586          
31587         this.progress = new Roo.bootstrap.Progress({
31588             cls : 'roo-document-manager-progress',
31589             active : true,
31590             striped : true
31591         });
31592         
31593         this.progress.render(this.progressDialog.getChildContainer());
31594         
31595         this.progressBar = new Roo.bootstrap.ProgressBar({
31596             cls : 'roo-document-manager-progress-bar',
31597             aria_valuenow : 0,
31598             aria_valuemin : 0,
31599             aria_valuemax : 12,
31600             panel : 'success'
31601         });
31602         
31603         this.progressBar.render(this.progress.getChildContainer());
31604     },
31605     
31606     onUploaderClick : function(e)
31607     {
31608         e.preventDefault();
31609      
31610         if(this.fireEvent('beforeselectfile', this) != false){
31611             this.selectorEl.dom.click();
31612         }
31613         
31614     },
31615     
31616     onFileSelected : function(e)
31617     {
31618         e.preventDefault();
31619         
31620         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31621             return;
31622         }
31623         
31624         Roo.each(this.selectorEl.dom.files, function(file){
31625             if(this.fireEvent('inspect', this, file) != false){
31626                 this.files.push(file);
31627             }
31628         }, this);
31629         
31630         this.queue();
31631         
31632     },
31633     
31634     queue : function()
31635     {
31636         this.selectorEl.dom.value = '';
31637         
31638         if(!this.files || !this.files.length){
31639             return;
31640         }
31641         
31642         if(this.boxes > 0 && this.files.length > this.boxes){
31643             this.files = this.files.slice(0, this.boxes);
31644         }
31645         
31646         this.uploader.show();
31647         
31648         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31649             this.uploader.hide();
31650         }
31651         
31652         var _this = this;
31653         
31654         var files = [];
31655         
31656         var docs = [];
31657         
31658         Roo.each(this.files, function(file){
31659             
31660             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31661                 var f = this.renderPreview(file);
31662                 files.push(f);
31663                 return;
31664             }
31665             
31666             if(file.type.indexOf('image') != -1){
31667                 this.delegates.push(
31668                     (function(){
31669                         _this.process(file);
31670                     }).createDelegate(this)
31671                 );
31672         
31673                 return;
31674             }
31675             
31676             docs.push(
31677                 (function(){
31678                     _this.process(file);
31679                 }).createDelegate(this)
31680             );
31681             
31682         }, this);
31683         
31684         this.files = files;
31685         
31686         this.delegates = this.delegates.concat(docs);
31687         
31688         if(!this.delegates.length){
31689             this.refresh();
31690             return;
31691         }
31692         
31693         this.progressBar.aria_valuemax = this.delegates.length;
31694         
31695         this.arrange();
31696         
31697         return;
31698     },
31699     
31700     arrange : function()
31701     {
31702         if(!this.delegates.length){
31703             this.progressDialog.hide();
31704             this.refresh();
31705             return;
31706         }
31707         
31708         var delegate = this.delegates.shift();
31709         
31710         this.progressDialog.show();
31711         
31712         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31713         
31714         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31715         
31716         delegate();
31717     },
31718     
31719     refresh : function()
31720     {
31721         this.uploader.show();
31722         
31723         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31724             this.uploader.hide();
31725         }
31726         
31727         Roo.isTouch ? this.closable(false) : this.closable(true);
31728         
31729         this.fireEvent('refresh', this);
31730     },
31731     
31732     onRemove : function(e, el, o)
31733     {
31734         e.preventDefault();
31735         
31736         this.fireEvent('remove', this, o);
31737         
31738     },
31739     
31740     remove : function(o)
31741     {
31742         var files = [];
31743         
31744         Roo.each(this.files, function(file){
31745             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31746                 files.push(file);
31747                 return;
31748             }
31749
31750             o.target.remove();
31751
31752         }, this);
31753         
31754         this.files = files;
31755         
31756         this.refresh();
31757     },
31758     
31759     clear : function()
31760     {
31761         Roo.each(this.files, function(file){
31762             if(!file.target){
31763                 return;
31764             }
31765             
31766             file.target.remove();
31767
31768         }, this);
31769         
31770         this.files = [];
31771         
31772         this.refresh();
31773     },
31774     
31775     onClick : function(e, el, o)
31776     {
31777         e.preventDefault();
31778         
31779         this.fireEvent('click', this, o);
31780         
31781     },
31782     
31783     closable : function(closable)
31784     {
31785         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31786             
31787             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31788             
31789             if(closable){
31790                 el.show();
31791                 return;
31792             }
31793             
31794             el.hide();
31795             
31796         }, this);
31797     },
31798     
31799     xhrOnLoad : function(xhr)
31800     {
31801         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31802             el.remove();
31803         }, this);
31804         
31805         if (xhr.readyState !== 4) {
31806             this.arrange();
31807             this.fireEvent('exception', this, xhr);
31808             return;
31809         }
31810
31811         var response = Roo.decode(xhr.responseText);
31812         
31813         if(!response.success){
31814             this.arrange();
31815             this.fireEvent('exception', this, xhr);
31816             return;
31817         }
31818         
31819         var file = this.renderPreview(response.data);
31820         
31821         this.files.push(file);
31822         
31823         this.arrange();
31824         
31825         this.fireEvent('afterupload', this, xhr);
31826         
31827     },
31828     
31829     xhrOnError : function(xhr)
31830     {
31831         Roo.log('xhr on error');
31832         
31833         var response = Roo.decode(xhr.responseText);
31834           
31835         Roo.log(response);
31836         
31837         this.arrange();
31838     },
31839     
31840     process : function(file)
31841     {
31842         if(this.fireEvent('process', this, file) !== false){
31843             if(this.editable && file.type.indexOf('image') != -1){
31844                 this.fireEvent('edit', this, file);
31845                 return;
31846             }
31847
31848             this.uploadStart(file, false);
31849
31850             return;
31851         }
31852         
31853     },
31854     
31855     uploadStart : function(file, crop)
31856     {
31857         this.xhr = new XMLHttpRequest();
31858         
31859         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31860             this.arrange();
31861             return;
31862         }
31863         
31864         file.xhr = this.xhr;
31865             
31866         this.managerEl.createChild({
31867             tag : 'div',
31868             cls : 'roo-document-manager-loading',
31869             cn : [
31870                 {
31871                     tag : 'div',
31872                     tooltip : file.name,
31873                     cls : 'roo-document-manager-thumb',
31874                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31875                 }
31876             ]
31877
31878         });
31879
31880         this.xhr.open(this.method, this.url, true);
31881         
31882         var headers = {
31883             "Accept": "application/json",
31884             "Cache-Control": "no-cache",
31885             "X-Requested-With": "XMLHttpRequest"
31886         };
31887         
31888         for (var headerName in headers) {
31889             var headerValue = headers[headerName];
31890             if (headerValue) {
31891                 this.xhr.setRequestHeader(headerName, headerValue);
31892             }
31893         }
31894         
31895         var _this = this;
31896         
31897         this.xhr.onload = function()
31898         {
31899             _this.xhrOnLoad(_this.xhr);
31900         }
31901         
31902         this.xhr.onerror = function()
31903         {
31904             _this.xhrOnError(_this.xhr);
31905         }
31906         
31907         var formData = new FormData();
31908
31909         formData.append('returnHTML', 'NO');
31910         
31911         if(crop){
31912             formData.append('crop', crop);
31913         }
31914         
31915         formData.append(this.paramName, file, file.name);
31916         
31917         var options = {
31918             file : file, 
31919             manually : false
31920         };
31921         
31922         if(this.fireEvent('prepare', this, formData, options) != false){
31923             
31924             if(options.manually){
31925                 return;
31926             }
31927             
31928             this.xhr.send(formData);
31929             return;
31930         };
31931         
31932         this.uploadCancel();
31933     },
31934     
31935     uploadCancel : function()
31936     {
31937         if (this.xhr) {
31938             this.xhr.abort();
31939         }
31940         
31941         this.delegates = [];
31942         
31943         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31944             el.remove();
31945         }, this);
31946         
31947         this.arrange();
31948     },
31949     
31950     renderPreview : function(file)
31951     {
31952         if(typeof(file.target) != 'undefined' && file.target){
31953             return file;
31954         }
31955         
31956         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31957         
31958         var previewEl = this.managerEl.createChild({
31959             tag : 'div',
31960             cls : 'roo-document-manager-preview',
31961             cn : [
31962                 {
31963                     tag : 'div',
31964                     tooltip : file[this.toolTipName],
31965                     cls : 'roo-document-manager-thumb',
31966                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31967                 },
31968                 {
31969                     tag : 'button',
31970                     cls : 'close',
31971                     html : '<i class="fa fa-times-circle"></i>'
31972                 }
31973             ]
31974         });
31975
31976         var close = previewEl.select('button.close', true).first();
31977
31978         close.on('click', this.onRemove, this, file);
31979
31980         file.target = previewEl;
31981
31982         var image = previewEl.select('img', true).first();
31983         
31984         var _this = this;
31985         
31986         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31987         
31988         image.on('click', this.onClick, this, file);
31989         
31990         this.fireEvent('previewrendered', this, file);
31991         
31992         return file;
31993         
31994     },
31995     
31996     onPreviewLoad : function(file, image)
31997     {
31998         if(typeof(file.target) == 'undefined' || !file.target){
31999             return;
32000         }
32001         
32002         var width = image.dom.naturalWidth || image.dom.width;
32003         var height = image.dom.naturalHeight || image.dom.height;
32004         
32005         if(!this.previewResize) {
32006             return;
32007         }
32008         
32009         if(width > height){
32010             file.target.addClass('wide');
32011             return;
32012         }
32013         
32014         file.target.addClass('tall');
32015         return;
32016         
32017     },
32018     
32019     uploadFromSource : function(file, crop)
32020     {
32021         this.xhr = new XMLHttpRequest();
32022         
32023         this.managerEl.createChild({
32024             tag : 'div',
32025             cls : 'roo-document-manager-loading',
32026             cn : [
32027                 {
32028                     tag : 'div',
32029                     tooltip : file.name,
32030                     cls : 'roo-document-manager-thumb',
32031                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32032                 }
32033             ]
32034
32035         });
32036
32037         this.xhr.open(this.method, this.url, true);
32038         
32039         var headers = {
32040             "Accept": "application/json",
32041             "Cache-Control": "no-cache",
32042             "X-Requested-With": "XMLHttpRequest"
32043         };
32044         
32045         for (var headerName in headers) {
32046             var headerValue = headers[headerName];
32047             if (headerValue) {
32048                 this.xhr.setRequestHeader(headerName, headerValue);
32049             }
32050         }
32051         
32052         var _this = this;
32053         
32054         this.xhr.onload = function()
32055         {
32056             _this.xhrOnLoad(_this.xhr);
32057         }
32058         
32059         this.xhr.onerror = function()
32060         {
32061             _this.xhrOnError(_this.xhr);
32062         }
32063         
32064         var formData = new FormData();
32065
32066         formData.append('returnHTML', 'NO');
32067         
32068         formData.append('crop', crop);
32069         
32070         if(typeof(file.filename) != 'undefined'){
32071             formData.append('filename', file.filename);
32072         }
32073         
32074         if(typeof(file.mimetype) != 'undefined'){
32075             formData.append('mimetype', file.mimetype);
32076         }
32077         
32078         Roo.log(formData);
32079         
32080         if(this.fireEvent('prepare', this, formData) != false){
32081             this.xhr.send(formData);
32082         };
32083     }
32084 });
32085
32086 /*
32087 * Licence: LGPL
32088 */
32089
32090 /**
32091  * @class Roo.bootstrap.DocumentViewer
32092  * @extends Roo.bootstrap.Component
32093  * Bootstrap DocumentViewer class
32094  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32095  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32096  * 
32097  * @constructor
32098  * Create a new DocumentViewer
32099  * @param {Object} config The config object
32100  */
32101
32102 Roo.bootstrap.DocumentViewer = function(config){
32103     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32104     
32105     this.addEvents({
32106         /**
32107          * @event initial
32108          * Fire after initEvent
32109          * @param {Roo.bootstrap.DocumentViewer} this
32110          */
32111         "initial" : true,
32112         /**
32113          * @event click
32114          * Fire after click
32115          * @param {Roo.bootstrap.DocumentViewer} this
32116          */
32117         "click" : true,
32118         /**
32119          * @event download
32120          * Fire after download button
32121          * @param {Roo.bootstrap.DocumentViewer} this
32122          */
32123         "download" : true,
32124         /**
32125          * @event trash
32126          * Fire after trash button
32127          * @param {Roo.bootstrap.DocumentViewer} this
32128          */
32129         "trash" : true
32130         
32131     });
32132 };
32133
32134 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32135     
32136     showDownload : true,
32137     
32138     showTrash : true,
32139     
32140     getAutoCreate : function()
32141     {
32142         var cfg = {
32143             tag : 'div',
32144             cls : 'roo-document-viewer',
32145             cn : [
32146                 {
32147                     tag : 'div',
32148                     cls : 'roo-document-viewer-body',
32149                     cn : [
32150                         {
32151                             tag : 'div',
32152                             cls : 'roo-document-viewer-thumb',
32153                             cn : [
32154                                 {
32155                                     tag : 'img',
32156                                     cls : 'roo-document-viewer-image'
32157                                 }
32158                             ]
32159                         }
32160                     ]
32161                 },
32162                 {
32163                     tag : 'div',
32164                     cls : 'roo-document-viewer-footer',
32165                     cn : {
32166                         tag : 'div',
32167                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32168                         cn : [
32169                             {
32170                                 tag : 'div',
32171                                 cls : 'btn-group roo-document-viewer-download',
32172                                 cn : [
32173                                     {
32174                                         tag : 'button',
32175                                         cls : 'btn btn-default',
32176                                         html : '<i class="fa fa-download"></i>'
32177                                     }
32178                                 ]
32179                             },
32180                             {
32181                                 tag : 'div',
32182                                 cls : 'btn-group roo-document-viewer-trash',
32183                                 cn : [
32184                                     {
32185                                         tag : 'button',
32186                                         cls : 'btn btn-default',
32187                                         html : '<i class="fa fa-trash"></i>'
32188                                     }
32189                                 ]
32190                             }
32191                         ]
32192                     }
32193                 }
32194             ]
32195         };
32196         
32197         return cfg;
32198     },
32199     
32200     initEvents : function()
32201     {
32202         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32203         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32204         
32205         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32206         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32207         
32208         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32209         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32210         
32211         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32212         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32213         
32214         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32215         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32216         
32217         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32218         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32219         
32220         this.bodyEl.on('click', this.onClick, this);
32221         this.downloadBtn.on('click', this.onDownload, this);
32222         this.trashBtn.on('click', this.onTrash, this);
32223         
32224         this.downloadBtn.hide();
32225         this.trashBtn.hide();
32226         
32227         if(this.showDownload){
32228             this.downloadBtn.show();
32229         }
32230         
32231         if(this.showTrash){
32232             this.trashBtn.show();
32233         }
32234         
32235         if(!this.showDownload && !this.showTrash) {
32236             this.footerEl.hide();
32237         }
32238         
32239     },
32240     
32241     initial : function()
32242     {
32243         this.fireEvent('initial', this);
32244         
32245     },
32246     
32247     onClick : function(e)
32248     {
32249         e.preventDefault();
32250         
32251         this.fireEvent('click', this);
32252     },
32253     
32254     onDownload : function(e)
32255     {
32256         e.preventDefault();
32257         
32258         this.fireEvent('download', this);
32259     },
32260     
32261     onTrash : function(e)
32262     {
32263         e.preventDefault();
32264         
32265         this.fireEvent('trash', this);
32266     }
32267     
32268 });
32269 /*
32270  * - LGPL
32271  *
32272  * nav progress bar
32273  * 
32274  */
32275
32276 /**
32277  * @class Roo.bootstrap.NavProgressBar
32278  * @extends Roo.bootstrap.Component
32279  * Bootstrap NavProgressBar class
32280  * 
32281  * @constructor
32282  * Create a new nav progress bar
32283  * @param {Object} config The config object
32284  */
32285
32286 Roo.bootstrap.NavProgressBar = function(config){
32287     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32288
32289     this.bullets = this.bullets || [];
32290    
32291 //    Roo.bootstrap.NavProgressBar.register(this);
32292      this.addEvents({
32293         /**
32294              * @event changed
32295              * Fires when the active item changes
32296              * @param {Roo.bootstrap.NavProgressBar} this
32297              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32298              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32299          */
32300         'changed': true
32301      });
32302     
32303 };
32304
32305 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32306     
32307     bullets : [],
32308     barItems : [],
32309     
32310     getAutoCreate : function()
32311     {
32312         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32313         
32314         cfg = {
32315             tag : 'div',
32316             cls : 'roo-navigation-bar-group',
32317             cn : [
32318                 {
32319                     tag : 'div',
32320                     cls : 'roo-navigation-top-bar'
32321                 },
32322                 {
32323                     tag : 'div',
32324                     cls : 'roo-navigation-bullets-bar',
32325                     cn : [
32326                         {
32327                             tag : 'ul',
32328                             cls : 'roo-navigation-bar'
32329                         }
32330                     ]
32331                 },
32332                 
32333                 {
32334                     tag : 'div',
32335                     cls : 'roo-navigation-bottom-bar'
32336                 }
32337             ]
32338             
32339         };
32340         
32341         return cfg;
32342         
32343     },
32344     
32345     initEvents: function() 
32346     {
32347         
32348     },
32349     
32350     onRender : function(ct, position) 
32351     {
32352         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32353         
32354         if(this.bullets.length){
32355             Roo.each(this.bullets, function(b){
32356                this.addItem(b);
32357             }, this);
32358         }
32359         
32360         this.format();
32361         
32362     },
32363     
32364     addItem : function(cfg)
32365     {
32366         var item = new Roo.bootstrap.NavProgressItem(cfg);
32367         
32368         item.parentId = this.id;
32369         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32370         
32371         if(cfg.html){
32372             var top = new Roo.bootstrap.Element({
32373                 tag : 'div',
32374                 cls : 'roo-navigation-bar-text'
32375             });
32376             
32377             var bottom = new Roo.bootstrap.Element({
32378                 tag : 'div',
32379                 cls : 'roo-navigation-bar-text'
32380             });
32381             
32382             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32383             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32384             
32385             var topText = new Roo.bootstrap.Element({
32386                 tag : 'span',
32387                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32388             });
32389             
32390             var bottomText = new Roo.bootstrap.Element({
32391                 tag : 'span',
32392                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32393             });
32394             
32395             topText.onRender(top.el, null);
32396             bottomText.onRender(bottom.el, null);
32397             
32398             item.topEl = top;
32399             item.bottomEl = bottom;
32400         }
32401         
32402         this.barItems.push(item);
32403         
32404         return item;
32405     },
32406     
32407     getActive : function()
32408     {
32409         var active = false;
32410         
32411         Roo.each(this.barItems, function(v){
32412             
32413             if (!v.isActive()) {
32414                 return;
32415             }
32416             
32417             active = v;
32418             return false;
32419             
32420         });
32421         
32422         return active;
32423     },
32424     
32425     setActiveItem : function(item)
32426     {
32427         var prev = false;
32428         
32429         Roo.each(this.barItems, function(v){
32430             if (v.rid == item.rid) {
32431                 return ;
32432             }
32433             
32434             if (v.isActive()) {
32435                 v.setActive(false);
32436                 prev = v;
32437             }
32438         });
32439
32440         item.setActive(true);
32441         
32442         this.fireEvent('changed', this, item, prev);
32443     },
32444     
32445     getBarItem: function(rid)
32446     {
32447         var ret = false;
32448         
32449         Roo.each(this.barItems, function(e) {
32450             if (e.rid != rid) {
32451                 return;
32452             }
32453             
32454             ret =  e;
32455             return false;
32456         });
32457         
32458         return ret;
32459     },
32460     
32461     indexOfItem : function(item)
32462     {
32463         var index = false;
32464         
32465         Roo.each(this.barItems, function(v, i){
32466             
32467             if (v.rid != item.rid) {
32468                 return;
32469             }
32470             
32471             index = i;
32472             return false
32473         });
32474         
32475         return index;
32476     },
32477     
32478     setActiveNext : function()
32479     {
32480         var i = this.indexOfItem(this.getActive());
32481         
32482         if (i > this.barItems.length) {
32483             return;
32484         }
32485         
32486         this.setActiveItem(this.barItems[i+1]);
32487     },
32488     
32489     setActivePrev : function()
32490     {
32491         var i = this.indexOfItem(this.getActive());
32492         
32493         if (i  < 1) {
32494             return;
32495         }
32496         
32497         this.setActiveItem(this.barItems[i-1]);
32498     },
32499     
32500     format : function()
32501     {
32502         if(!this.barItems.length){
32503             return;
32504         }
32505      
32506         var width = 100 / this.barItems.length;
32507         
32508         Roo.each(this.barItems, function(i){
32509             i.el.setStyle('width', width + '%');
32510             i.topEl.el.setStyle('width', width + '%');
32511             i.bottomEl.el.setStyle('width', width + '%');
32512         }, this);
32513         
32514     }
32515     
32516 });
32517 /*
32518  * - LGPL
32519  *
32520  * Nav Progress Item
32521  * 
32522  */
32523
32524 /**
32525  * @class Roo.bootstrap.NavProgressItem
32526  * @extends Roo.bootstrap.Component
32527  * Bootstrap NavProgressItem class
32528  * @cfg {String} rid the reference id
32529  * @cfg {Boolean} active (true|false) Is item active default false
32530  * @cfg {Boolean} disabled (true|false) Is item active default false
32531  * @cfg {String} html
32532  * @cfg {String} position (top|bottom) text position default bottom
32533  * @cfg {String} icon show icon instead of number
32534  * 
32535  * @constructor
32536  * Create a new NavProgressItem
32537  * @param {Object} config The config object
32538  */
32539 Roo.bootstrap.NavProgressItem = function(config){
32540     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32541     this.addEvents({
32542         // raw events
32543         /**
32544          * @event click
32545          * The raw click event for the entire grid.
32546          * @param {Roo.bootstrap.NavProgressItem} this
32547          * @param {Roo.EventObject} e
32548          */
32549         "click" : true
32550     });
32551    
32552 };
32553
32554 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32555     
32556     rid : '',
32557     active : false,
32558     disabled : false,
32559     html : '',
32560     position : 'bottom',
32561     icon : false,
32562     
32563     getAutoCreate : function()
32564     {
32565         var iconCls = 'roo-navigation-bar-item-icon';
32566         
32567         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32568         
32569         var cfg = {
32570             tag: 'li',
32571             cls: 'roo-navigation-bar-item',
32572             cn : [
32573                 {
32574                     tag : 'i',
32575                     cls : iconCls
32576                 }
32577             ]
32578         };
32579         
32580         if(this.active){
32581             cfg.cls += ' active';
32582         }
32583         if(this.disabled){
32584             cfg.cls += ' disabled';
32585         }
32586         
32587         return cfg;
32588     },
32589     
32590     disable : function()
32591     {
32592         this.setDisabled(true);
32593     },
32594     
32595     enable : function()
32596     {
32597         this.setDisabled(false);
32598     },
32599     
32600     initEvents: function() 
32601     {
32602         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32603         
32604         this.iconEl.on('click', this.onClick, this);
32605     },
32606     
32607     onClick : function(e)
32608     {
32609         e.preventDefault();
32610         
32611         if(this.disabled){
32612             return;
32613         }
32614         
32615         if(this.fireEvent('click', this, e) === false){
32616             return;
32617         };
32618         
32619         this.parent().setActiveItem(this);
32620     },
32621     
32622     isActive: function () 
32623     {
32624         return this.active;
32625     },
32626     
32627     setActive : function(state)
32628     {
32629         if(this.active == state){
32630             return;
32631         }
32632         
32633         this.active = state;
32634         
32635         if (state) {
32636             this.el.addClass('active');
32637             return;
32638         }
32639         
32640         this.el.removeClass('active');
32641         
32642         return;
32643     },
32644     
32645     setDisabled : function(state)
32646     {
32647         if(this.disabled == state){
32648             return;
32649         }
32650         
32651         this.disabled = state;
32652         
32653         if (state) {
32654             this.el.addClass('disabled');
32655             return;
32656         }
32657         
32658         this.el.removeClass('disabled');
32659     },
32660     
32661     tooltipEl : function()
32662     {
32663         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32664     }
32665 });
32666  
32667
32668  /*
32669  * - LGPL
32670  *
32671  * FieldLabel
32672  * 
32673  */
32674
32675 /**
32676  * @class Roo.bootstrap.FieldLabel
32677  * @extends Roo.bootstrap.Component
32678  * Bootstrap FieldLabel class
32679  * @cfg {String} html contents of the element
32680  * @cfg {String} tag tag of the element default label
32681  * @cfg {String} cls class of the element
32682  * @cfg {String} target label target 
32683  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32684  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32685  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32686  * @cfg {String} iconTooltip default "This field is required"
32687  * @cfg {String} indicatorpos (left|right) default left
32688  * 
32689  * @constructor
32690  * Create a new FieldLabel
32691  * @param {Object} config The config object
32692  */
32693
32694 Roo.bootstrap.FieldLabel = function(config){
32695     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32696     
32697     this.addEvents({
32698             /**
32699              * @event invalid
32700              * Fires after the field has been marked as invalid.
32701              * @param {Roo.form.FieldLabel} this
32702              * @param {String} msg The validation message
32703              */
32704             invalid : true,
32705             /**
32706              * @event valid
32707              * Fires after the field has been validated with no errors.
32708              * @param {Roo.form.FieldLabel} this
32709              */
32710             valid : true
32711         });
32712 };
32713
32714 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32715     
32716     tag: 'label',
32717     cls: '',
32718     html: '',
32719     target: '',
32720     allowBlank : true,
32721     invalidClass : 'has-warning',
32722     validClass : 'has-success',
32723     iconTooltip : 'This field is required',
32724     indicatorpos : 'left',
32725     
32726     getAutoCreate : function(){
32727         
32728         var cls = "";
32729         if (!this.allowBlank) {
32730             cls  = "visible";
32731         }
32732         
32733         var cfg = {
32734             tag : this.tag,
32735             cls : 'roo-bootstrap-field-label ' + this.cls,
32736             for : this.target,
32737             cn : [
32738                 {
32739                     tag : 'i',
32740                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32741                     tooltip : this.iconTooltip
32742                 },
32743                 {
32744                     tag : 'span',
32745                     html : this.html
32746                 }
32747             ] 
32748         };
32749         
32750         if(this.indicatorpos == 'right'){
32751             var cfg = {
32752                 tag : this.tag,
32753                 cls : 'roo-bootstrap-field-label ' + this.cls,
32754                 for : this.target,
32755                 cn : [
32756                     {
32757                         tag : 'span',
32758                         html : this.html
32759                     },
32760                     {
32761                         tag : 'i',
32762                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32763                         tooltip : this.iconTooltip
32764                     }
32765                 ] 
32766             };
32767         }
32768         
32769         return cfg;
32770     },
32771     
32772     initEvents: function() 
32773     {
32774         Roo.bootstrap.Element.superclass.initEvents.call(this);
32775         
32776         this.indicator = this.indicatorEl();
32777         
32778         if(this.indicator){
32779             this.indicator.removeClass('visible');
32780             this.indicator.addClass('invisible');
32781         }
32782         
32783         Roo.bootstrap.FieldLabel.register(this);
32784     },
32785     
32786     indicatorEl : function()
32787     {
32788         var indicator = this.el.select('i.roo-required-indicator',true).first();
32789         
32790         if(!indicator){
32791             return false;
32792         }
32793         
32794         return indicator;
32795         
32796     },
32797     
32798     /**
32799      * Mark this field as valid
32800      */
32801     markValid : function()
32802     {
32803         if(this.indicator){
32804             this.indicator.removeClass('visible');
32805             this.indicator.addClass('invisible');
32806         }
32807         if (Roo.bootstrap.version == 3) {
32808             this.el.removeClass(this.invalidClass);
32809             this.el.addClass(this.validClass);
32810         } else {
32811             this.el.removeClass('is-invalid');
32812             this.el.addClass('is-valid');
32813         }
32814         
32815         
32816         this.fireEvent('valid', this);
32817     },
32818     
32819     /**
32820      * Mark this field as invalid
32821      * @param {String} msg The validation message
32822      */
32823     markInvalid : function(msg)
32824     {
32825         if(this.indicator){
32826             this.indicator.removeClass('invisible');
32827             this.indicator.addClass('visible');
32828         }
32829           if (Roo.bootstrap.version == 3) {
32830             this.el.removeClass(this.validClass);
32831             this.el.addClass(this.invalidClass);
32832         } else {
32833             this.el.removeClass('is-valid');
32834             this.el.addClass('is-invalid');
32835         }
32836         
32837         
32838         this.fireEvent('invalid', this, msg);
32839     }
32840     
32841    
32842 });
32843
32844 Roo.apply(Roo.bootstrap.FieldLabel, {
32845     
32846     groups: {},
32847     
32848      /**
32849     * register a FieldLabel Group
32850     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32851     */
32852     register : function(label)
32853     {
32854         if(this.groups.hasOwnProperty(label.target)){
32855             return;
32856         }
32857      
32858         this.groups[label.target] = label;
32859         
32860     },
32861     /**
32862     * fetch a FieldLabel Group based on the target
32863     * @param {string} target
32864     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32865     */
32866     get: function(target) {
32867         if (typeof(this.groups[target]) == 'undefined') {
32868             return false;
32869         }
32870         
32871         return this.groups[target] ;
32872     }
32873 });
32874
32875  
32876
32877  /*
32878  * - LGPL
32879  *
32880  * page DateSplitField.
32881  * 
32882  */
32883
32884
32885 /**
32886  * @class Roo.bootstrap.DateSplitField
32887  * @extends Roo.bootstrap.Component
32888  * Bootstrap DateSplitField class
32889  * @cfg {string} fieldLabel - the label associated
32890  * @cfg {Number} labelWidth set the width of label (0-12)
32891  * @cfg {String} labelAlign (top|left)
32892  * @cfg {Boolean} dayAllowBlank (true|false) default false
32893  * @cfg {Boolean} monthAllowBlank (true|false) default false
32894  * @cfg {Boolean} yearAllowBlank (true|false) default false
32895  * @cfg {string} dayPlaceholder 
32896  * @cfg {string} monthPlaceholder
32897  * @cfg {string} yearPlaceholder
32898  * @cfg {string} dayFormat default 'd'
32899  * @cfg {string} monthFormat default 'm'
32900  * @cfg {string} yearFormat default 'Y'
32901  * @cfg {Number} labellg set the width of label (1-12)
32902  * @cfg {Number} labelmd set the width of label (1-12)
32903  * @cfg {Number} labelsm set the width of label (1-12)
32904  * @cfg {Number} labelxs set the width of label (1-12)
32905
32906  *     
32907  * @constructor
32908  * Create a new DateSplitField
32909  * @param {Object} config The config object
32910  */
32911
32912 Roo.bootstrap.DateSplitField = function(config){
32913     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32914     
32915     this.addEvents({
32916         // raw events
32917          /**
32918          * @event years
32919          * getting the data of years
32920          * @param {Roo.bootstrap.DateSplitField} this
32921          * @param {Object} years
32922          */
32923         "years" : true,
32924         /**
32925          * @event days
32926          * getting the data of days
32927          * @param {Roo.bootstrap.DateSplitField} this
32928          * @param {Object} days
32929          */
32930         "days" : true,
32931         /**
32932          * @event invalid
32933          * Fires after the field has been marked as invalid.
32934          * @param {Roo.form.Field} this
32935          * @param {String} msg The validation message
32936          */
32937         invalid : true,
32938        /**
32939          * @event valid
32940          * Fires after the field has been validated with no errors.
32941          * @param {Roo.form.Field} this
32942          */
32943         valid : true
32944     });
32945 };
32946
32947 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32948     
32949     fieldLabel : '',
32950     labelAlign : 'top',
32951     labelWidth : 3,
32952     dayAllowBlank : false,
32953     monthAllowBlank : false,
32954     yearAllowBlank : false,
32955     dayPlaceholder : '',
32956     monthPlaceholder : '',
32957     yearPlaceholder : '',
32958     dayFormat : 'd',
32959     monthFormat : 'm',
32960     yearFormat : 'Y',
32961     isFormField : true,
32962     labellg : 0,
32963     labelmd : 0,
32964     labelsm : 0,
32965     labelxs : 0,
32966     
32967     getAutoCreate : function()
32968     {
32969         var cfg = {
32970             tag : 'div',
32971             cls : 'row roo-date-split-field-group',
32972             cn : [
32973                 {
32974                     tag : 'input',
32975                     type : 'hidden',
32976                     cls : 'form-hidden-field roo-date-split-field-group-value',
32977                     name : this.name
32978                 }
32979             ]
32980         };
32981         
32982         var labelCls = 'col-md-12';
32983         var contentCls = 'col-md-4';
32984         
32985         if(this.fieldLabel){
32986             
32987             var label = {
32988                 tag : 'div',
32989                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32990                 cn : [
32991                     {
32992                         tag : 'label',
32993                         html : this.fieldLabel
32994                     }
32995                 ]
32996             };
32997             
32998             if(this.labelAlign == 'left'){
32999             
33000                 if(this.labelWidth > 12){
33001                     label.style = "width: " + this.labelWidth + 'px';
33002                 }
33003
33004                 if(this.labelWidth < 13 && this.labelmd == 0){
33005                     this.labelmd = this.labelWidth;
33006                 }
33007
33008                 if(this.labellg > 0){
33009                     labelCls = ' col-lg-' + this.labellg;
33010                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33011                 }
33012
33013                 if(this.labelmd > 0){
33014                     labelCls = ' col-md-' + this.labelmd;
33015                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33016                 }
33017
33018                 if(this.labelsm > 0){
33019                     labelCls = ' col-sm-' + this.labelsm;
33020                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33021                 }
33022
33023                 if(this.labelxs > 0){
33024                     labelCls = ' col-xs-' + this.labelxs;
33025                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33026                 }
33027             }
33028             
33029             label.cls += ' ' + labelCls;
33030             
33031             cfg.cn.push(label);
33032         }
33033         
33034         Roo.each(['day', 'month', 'year'], function(t){
33035             cfg.cn.push({
33036                 tag : 'div',
33037                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33038             });
33039         }, this);
33040         
33041         return cfg;
33042     },
33043     
33044     inputEl: function ()
33045     {
33046         return this.el.select('.roo-date-split-field-group-value', true).first();
33047     },
33048     
33049     onRender : function(ct, position) 
33050     {
33051         var _this = this;
33052         
33053         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33054         
33055         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33056         
33057         this.dayField = new Roo.bootstrap.ComboBox({
33058             allowBlank : this.dayAllowBlank,
33059             alwaysQuery : true,
33060             displayField : 'value',
33061             editable : false,
33062             fieldLabel : '',
33063             forceSelection : true,
33064             mode : 'local',
33065             placeholder : this.dayPlaceholder,
33066             selectOnFocus : true,
33067             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33068             triggerAction : 'all',
33069             typeAhead : true,
33070             valueField : 'value',
33071             store : new Roo.data.SimpleStore({
33072                 data : (function() {    
33073                     var days = [];
33074                     _this.fireEvent('days', _this, days);
33075                     return days;
33076                 })(),
33077                 fields : [ 'value' ]
33078             }),
33079             listeners : {
33080                 select : function (_self, record, index)
33081                 {
33082                     _this.setValue(_this.getValue());
33083                 }
33084             }
33085         });
33086
33087         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33088         
33089         this.monthField = new Roo.bootstrap.MonthField({
33090             after : '<i class=\"fa fa-calendar\"></i>',
33091             allowBlank : this.monthAllowBlank,
33092             placeholder : this.monthPlaceholder,
33093             readOnly : true,
33094             listeners : {
33095                 render : function (_self)
33096                 {
33097                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33098                         e.preventDefault();
33099                         _self.focus();
33100                     });
33101                 },
33102                 select : function (_self, oldvalue, newvalue)
33103                 {
33104                     _this.setValue(_this.getValue());
33105                 }
33106             }
33107         });
33108         
33109         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33110         
33111         this.yearField = new Roo.bootstrap.ComboBox({
33112             allowBlank : this.yearAllowBlank,
33113             alwaysQuery : true,
33114             displayField : 'value',
33115             editable : false,
33116             fieldLabel : '',
33117             forceSelection : true,
33118             mode : 'local',
33119             placeholder : this.yearPlaceholder,
33120             selectOnFocus : true,
33121             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33122             triggerAction : 'all',
33123             typeAhead : true,
33124             valueField : 'value',
33125             store : new Roo.data.SimpleStore({
33126                 data : (function() {
33127                     var years = [];
33128                     _this.fireEvent('years', _this, years);
33129                     return years;
33130                 })(),
33131                 fields : [ 'value' ]
33132             }),
33133             listeners : {
33134                 select : function (_self, record, index)
33135                 {
33136                     _this.setValue(_this.getValue());
33137                 }
33138             }
33139         });
33140
33141         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33142     },
33143     
33144     setValue : function(v, format)
33145     {
33146         this.inputEl.dom.value = v;
33147         
33148         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33149         
33150         var d = Date.parseDate(v, f);
33151         
33152         if(!d){
33153             this.validate();
33154             return;
33155         }
33156         
33157         this.setDay(d.format(this.dayFormat));
33158         this.setMonth(d.format(this.monthFormat));
33159         this.setYear(d.format(this.yearFormat));
33160         
33161         this.validate();
33162         
33163         return;
33164     },
33165     
33166     setDay : function(v)
33167     {
33168         this.dayField.setValue(v);
33169         this.inputEl.dom.value = this.getValue();
33170         this.validate();
33171         return;
33172     },
33173     
33174     setMonth : function(v)
33175     {
33176         this.monthField.setValue(v, true);
33177         this.inputEl.dom.value = this.getValue();
33178         this.validate();
33179         return;
33180     },
33181     
33182     setYear : function(v)
33183     {
33184         this.yearField.setValue(v);
33185         this.inputEl.dom.value = this.getValue();
33186         this.validate();
33187         return;
33188     },
33189     
33190     getDay : function()
33191     {
33192         return this.dayField.getValue();
33193     },
33194     
33195     getMonth : function()
33196     {
33197         return this.monthField.getValue();
33198     },
33199     
33200     getYear : function()
33201     {
33202         return this.yearField.getValue();
33203     },
33204     
33205     getValue : function()
33206     {
33207         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33208         
33209         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33210         
33211         return date;
33212     },
33213     
33214     reset : function()
33215     {
33216         this.setDay('');
33217         this.setMonth('');
33218         this.setYear('');
33219         this.inputEl.dom.value = '';
33220         this.validate();
33221         return;
33222     },
33223     
33224     validate : function()
33225     {
33226         var d = this.dayField.validate();
33227         var m = this.monthField.validate();
33228         var y = this.yearField.validate();
33229         
33230         var valid = true;
33231         
33232         if(
33233                 (!this.dayAllowBlank && !d) ||
33234                 (!this.monthAllowBlank && !m) ||
33235                 (!this.yearAllowBlank && !y)
33236         ){
33237             valid = false;
33238         }
33239         
33240         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33241             return valid;
33242         }
33243         
33244         if(valid){
33245             this.markValid();
33246             return valid;
33247         }
33248         
33249         this.markInvalid();
33250         
33251         return valid;
33252     },
33253     
33254     markValid : function()
33255     {
33256         
33257         var label = this.el.select('label', true).first();
33258         var icon = this.el.select('i.fa-star', true).first();
33259
33260         if(label && icon){
33261             icon.remove();
33262         }
33263         
33264         this.fireEvent('valid', this);
33265     },
33266     
33267      /**
33268      * Mark this field as invalid
33269      * @param {String} msg The validation message
33270      */
33271     markInvalid : function(msg)
33272     {
33273         
33274         var label = this.el.select('label', true).first();
33275         var icon = this.el.select('i.fa-star', true).first();
33276
33277         if(label && !icon){
33278             this.el.select('.roo-date-split-field-label', true).createChild({
33279                 tag : 'i',
33280                 cls : 'text-danger fa fa-lg fa-star',
33281                 tooltip : 'This field is required',
33282                 style : 'margin-right:5px;'
33283             }, label, true);
33284         }
33285         
33286         this.fireEvent('invalid', this, msg);
33287     },
33288     
33289     clearInvalid : function()
33290     {
33291         var label = this.el.select('label', true).first();
33292         var icon = this.el.select('i.fa-star', true).first();
33293
33294         if(label && icon){
33295             icon.remove();
33296         }
33297         
33298         this.fireEvent('valid', this);
33299     },
33300     
33301     getName: function()
33302     {
33303         return this.name;
33304     }
33305     
33306 });
33307
33308  /**
33309  *
33310  * This is based on 
33311  * http://masonry.desandro.com
33312  *
33313  * The idea is to render all the bricks based on vertical width...
33314  *
33315  * The original code extends 'outlayer' - we might need to use that....
33316  * 
33317  */
33318
33319
33320 /**
33321  * @class Roo.bootstrap.LayoutMasonry
33322  * @extends Roo.bootstrap.Component
33323  * Bootstrap Layout Masonry class
33324  * 
33325  * @constructor
33326  * Create a new Element
33327  * @param {Object} config The config object
33328  */
33329
33330 Roo.bootstrap.LayoutMasonry = function(config){
33331     
33332     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33333     
33334     this.bricks = [];
33335     
33336     Roo.bootstrap.LayoutMasonry.register(this);
33337     
33338     this.addEvents({
33339         // raw events
33340         /**
33341          * @event layout
33342          * Fire after layout the items
33343          * @param {Roo.bootstrap.LayoutMasonry} this
33344          * @param {Roo.EventObject} e
33345          */
33346         "layout" : true
33347     });
33348     
33349 };
33350
33351 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33352     
33353     /**
33354      * @cfg {Boolean} isLayoutInstant = no animation?
33355      */   
33356     isLayoutInstant : false, // needed?
33357    
33358     /**
33359      * @cfg {Number} boxWidth  width of the columns
33360      */   
33361     boxWidth : 450,
33362     
33363       /**
33364      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33365      */   
33366     boxHeight : 0,
33367     
33368     /**
33369      * @cfg {Number} padWidth padding below box..
33370      */   
33371     padWidth : 10, 
33372     
33373     /**
33374      * @cfg {Number} gutter gutter width..
33375      */   
33376     gutter : 10,
33377     
33378      /**
33379      * @cfg {Number} maxCols maximum number of columns
33380      */   
33381     
33382     maxCols: 0,
33383     
33384     /**
33385      * @cfg {Boolean} isAutoInitial defalut true
33386      */   
33387     isAutoInitial : true, 
33388     
33389     containerWidth: 0,
33390     
33391     /**
33392      * @cfg {Boolean} isHorizontal defalut false
33393      */   
33394     isHorizontal : false, 
33395
33396     currentSize : null,
33397     
33398     tag: 'div',
33399     
33400     cls: '',
33401     
33402     bricks: null, //CompositeElement
33403     
33404     cols : 1,
33405     
33406     _isLayoutInited : false,
33407     
33408 //    isAlternative : false, // only use for vertical layout...
33409     
33410     /**
33411      * @cfg {Number} alternativePadWidth padding below box..
33412      */   
33413     alternativePadWidth : 50,
33414     
33415     selectedBrick : [],
33416     
33417     getAutoCreate : function(){
33418         
33419         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33420         
33421         var cfg = {
33422             tag: this.tag,
33423             cls: 'blog-masonary-wrapper ' + this.cls,
33424             cn : {
33425                 cls : 'mas-boxes masonary'
33426             }
33427         };
33428         
33429         return cfg;
33430     },
33431     
33432     getChildContainer: function( )
33433     {
33434         if (this.boxesEl) {
33435             return this.boxesEl;
33436         }
33437         
33438         this.boxesEl = this.el.select('.mas-boxes').first();
33439         
33440         return this.boxesEl;
33441     },
33442     
33443     
33444     initEvents : function()
33445     {
33446         var _this = this;
33447         
33448         if(this.isAutoInitial){
33449             Roo.log('hook children rendered');
33450             this.on('childrenrendered', function() {
33451                 Roo.log('children rendered');
33452                 _this.initial();
33453             } ,this);
33454         }
33455     },
33456     
33457     initial : function()
33458     {
33459         this.selectedBrick = [];
33460         
33461         this.currentSize = this.el.getBox(true);
33462         
33463         Roo.EventManager.onWindowResize(this.resize, this); 
33464
33465         if(!this.isAutoInitial){
33466             this.layout();
33467             return;
33468         }
33469         
33470         this.layout();
33471         
33472         return;
33473         //this.layout.defer(500,this);
33474         
33475     },
33476     
33477     resize : function()
33478     {
33479         var cs = this.el.getBox(true);
33480         
33481         if (
33482                 this.currentSize.width == cs.width && 
33483                 this.currentSize.x == cs.x && 
33484                 this.currentSize.height == cs.height && 
33485                 this.currentSize.y == cs.y 
33486         ) {
33487             Roo.log("no change in with or X or Y");
33488             return;
33489         }
33490         
33491         this.currentSize = cs;
33492         
33493         this.layout();
33494         
33495     },
33496     
33497     layout : function()
33498     {   
33499         this._resetLayout();
33500         
33501         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33502         
33503         this.layoutItems( isInstant );
33504       
33505         this._isLayoutInited = true;
33506         
33507         this.fireEvent('layout', this);
33508         
33509     },
33510     
33511     _resetLayout : function()
33512     {
33513         if(this.isHorizontal){
33514             this.horizontalMeasureColumns();
33515             return;
33516         }
33517         
33518         this.verticalMeasureColumns();
33519         
33520     },
33521     
33522     verticalMeasureColumns : function()
33523     {
33524         this.getContainerWidth();
33525         
33526 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33527 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33528 //            return;
33529 //        }
33530         
33531         var boxWidth = this.boxWidth + this.padWidth;
33532         
33533         if(this.containerWidth < this.boxWidth){
33534             boxWidth = this.containerWidth
33535         }
33536         
33537         var containerWidth = this.containerWidth;
33538         
33539         var cols = Math.floor(containerWidth / boxWidth);
33540         
33541         this.cols = Math.max( cols, 1 );
33542         
33543         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33544         
33545         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33546         
33547         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33548         
33549         this.colWidth = boxWidth + avail - this.padWidth;
33550         
33551         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33552         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33553     },
33554     
33555     horizontalMeasureColumns : function()
33556     {
33557         this.getContainerWidth();
33558         
33559         var boxWidth = this.boxWidth;
33560         
33561         if(this.containerWidth < boxWidth){
33562             boxWidth = this.containerWidth;
33563         }
33564         
33565         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33566         
33567         this.el.setHeight(boxWidth);
33568         
33569     },
33570     
33571     getContainerWidth : function()
33572     {
33573         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33574     },
33575     
33576     layoutItems : function( isInstant )
33577     {
33578         Roo.log(this.bricks);
33579         
33580         var items = Roo.apply([], this.bricks);
33581         
33582         if(this.isHorizontal){
33583             this._horizontalLayoutItems( items , isInstant );
33584             return;
33585         }
33586         
33587 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33588 //            this._verticalAlternativeLayoutItems( items , isInstant );
33589 //            return;
33590 //        }
33591         
33592         this._verticalLayoutItems( items , isInstant );
33593         
33594     },
33595     
33596     _verticalLayoutItems : function ( items , isInstant)
33597     {
33598         if ( !items || !items.length ) {
33599             return;
33600         }
33601         
33602         var standard = [
33603             ['xs', 'xs', 'xs', 'tall'],
33604             ['xs', 'xs', 'tall'],
33605             ['xs', 'xs', 'sm'],
33606             ['xs', 'xs', 'xs'],
33607             ['xs', 'tall'],
33608             ['xs', 'sm'],
33609             ['xs', 'xs'],
33610             ['xs'],
33611             
33612             ['sm', 'xs', 'xs'],
33613             ['sm', 'xs'],
33614             ['sm'],
33615             
33616             ['tall', 'xs', 'xs', 'xs'],
33617             ['tall', 'xs', 'xs'],
33618             ['tall', 'xs'],
33619             ['tall']
33620             
33621         ];
33622         
33623         var queue = [];
33624         
33625         var boxes = [];
33626         
33627         var box = [];
33628         
33629         Roo.each(items, function(item, k){
33630             
33631             switch (item.size) {
33632                 // these layouts take up a full box,
33633                 case 'md' :
33634                 case 'md-left' :
33635                 case 'md-right' :
33636                 case 'wide' :
33637                     
33638                     if(box.length){
33639                         boxes.push(box);
33640                         box = [];
33641                     }
33642                     
33643                     boxes.push([item]);
33644                     
33645                     break;
33646                     
33647                 case 'xs' :
33648                 case 'sm' :
33649                 case 'tall' :
33650                     
33651                     box.push(item);
33652                     
33653                     break;
33654                 default :
33655                     break;
33656                     
33657             }
33658             
33659         }, this);
33660         
33661         if(box.length){
33662             boxes.push(box);
33663             box = [];
33664         }
33665         
33666         var filterPattern = function(box, length)
33667         {
33668             if(!box.length){
33669                 return;
33670             }
33671             
33672             var match = false;
33673             
33674             var pattern = box.slice(0, length);
33675             
33676             var format = [];
33677             
33678             Roo.each(pattern, function(i){
33679                 format.push(i.size);
33680             }, this);
33681             
33682             Roo.each(standard, function(s){
33683                 
33684                 if(String(s) != String(format)){
33685                     return;
33686                 }
33687                 
33688                 match = true;
33689                 return false;
33690                 
33691             }, this);
33692             
33693             if(!match && length == 1){
33694                 return;
33695             }
33696             
33697             if(!match){
33698                 filterPattern(box, length - 1);
33699                 return;
33700             }
33701                 
33702             queue.push(pattern);
33703
33704             box = box.slice(length, box.length);
33705
33706             filterPattern(box, 4);
33707
33708             return;
33709             
33710         }
33711         
33712         Roo.each(boxes, function(box, k){
33713             
33714             if(!box.length){
33715                 return;
33716             }
33717             
33718             if(box.length == 1){
33719                 queue.push(box);
33720                 return;
33721             }
33722             
33723             filterPattern(box, 4);
33724             
33725         }, this);
33726         
33727         this._processVerticalLayoutQueue( queue, isInstant );
33728         
33729     },
33730     
33731 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33732 //    {
33733 //        if ( !items || !items.length ) {
33734 //            return;
33735 //        }
33736 //
33737 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33738 //        
33739 //    },
33740     
33741     _horizontalLayoutItems : function ( items , isInstant)
33742     {
33743         if ( !items || !items.length || items.length < 3) {
33744             return;
33745         }
33746         
33747         items.reverse();
33748         
33749         var eItems = items.slice(0, 3);
33750         
33751         items = items.slice(3, items.length);
33752         
33753         var standard = [
33754             ['xs', 'xs', 'xs', 'wide'],
33755             ['xs', 'xs', 'wide'],
33756             ['xs', 'xs', 'sm'],
33757             ['xs', 'xs', 'xs'],
33758             ['xs', 'wide'],
33759             ['xs', 'sm'],
33760             ['xs', 'xs'],
33761             ['xs'],
33762             
33763             ['sm', 'xs', 'xs'],
33764             ['sm', 'xs'],
33765             ['sm'],
33766             
33767             ['wide', 'xs', 'xs', 'xs'],
33768             ['wide', 'xs', 'xs'],
33769             ['wide', 'xs'],
33770             ['wide'],
33771             
33772             ['wide-thin']
33773         ];
33774         
33775         var queue = [];
33776         
33777         var boxes = [];
33778         
33779         var box = [];
33780         
33781         Roo.each(items, function(item, k){
33782             
33783             switch (item.size) {
33784                 case 'md' :
33785                 case 'md-left' :
33786                 case 'md-right' :
33787                 case 'tall' :
33788                     
33789                     if(box.length){
33790                         boxes.push(box);
33791                         box = [];
33792                     }
33793                     
33794                     boxes.push([item]);
33795                     
33796                     break;
33797                     
33798                 case 'xs' :
33799                 case 'sm' :
33800                 case 'wide' :
33801                 case 'wide-thin' :
33802                     
33803                     box.push(item);
33804                     
33805                     break;
33806                 default :
33807                     break;
33808                     
33809             }
33810             
33811         }, this);
33812         
33813         if(box.length){
33814             boxes.push(box);
33815             box = [];
33816         }
33817         
33818         var filterPattern = function(box, length)
33819         {
33820             if(!box.length){
33821                 return;
33822             }
33823             
33824             var match = false;
33825             
33826             var pattern = box.slice(0, length);
33827             
33828             var format = [];
33829             
33830             Roo.each(pattern, function(i){
33831                 format.push(i.size);
33832             }, this);
33833             
33834             Roo.each(standard, function(s){
33835                 
33836                 if(String(s) != String(format)){
33837                     return;
33838                 }
33839                 
33840                 match = true;
33841                 return false;
33842                 
33843             }, this);
33844             
33845             if(!match && length == 1){
33846                 return;
33847             }
33848             
33849             if(!match){
33850                 filterPattern(box, length - 1);
33851                 return;
33852             }
33853                 
33854             queue.push(pattern);
33855
33856             box = box.slice(length, box.length);
33857
33858             filterPattern(box, 4);
33859
33860             return;
33861             
33862         }
33863         
33864         Roo.each(boxes, function(box, k){
33865             
33866             if(!box.length){
33867                 return;
33868             }
33869             
33870             if(box.length == 1){
33871                 queue.push(box);
33872                 return;
33873             }
33874             
33875             filterPattern(box, 4);
33876             
33877         }, this);
33878         
33879         
33880         var prune = [];
33881         
33882         var pos = this.el.getBox(true);
33883         
33884         var minX = pos.x;
33885         
33886         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33887         
33888         var hit_end = false;
33889         
33890         Roo.each(queue, function(box){
33891             
33892             if(hit_end){
33893                 
33894                 Roo.each(box, function(b){
33895                 
33896                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33897                     b.el.hide();
33898
33899                 }, this);
33900
33901                 return;
33902             }
33903             
33904             var mx = 0;
33905             
33906             Roo.each(box, function(b){
33907                 
33908                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33909                 b.el.show();
33910
33911                 mx = Math.max(mx, b.x);
33912                 
33913             }, this);
33914             
33915             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33916             
33917             if(maxX < minX){
33918                 
33919                 Roo.each(box, function(b){
33920                 
33921                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33922                     b.el.hide();
33923                     
33924                 }, this);
33925                 
33926                 hit_end = true;
33927                 
33928                 return;
33929             }
33930             
33931             prune.push(box);
33932             
33933         }, this);
33934         
33935         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33936     },
33937     
33938     /** Sets position of item in DOM
33939     * @param {Element} item
33940     * @param {Number} x - horizontal position
33941     * @param {Number} y - vertical position
33942     * @param {Boolean} isInstant - disables transitions
33943     */
33944     _processVerticalLayoutQueue : function( queue, isInstant )
33945     {
33946         var pos = this.el.getBox(true);
33947         var x = pos.x;
33948         var y = pos.y;
33949         var maxY = [];
33950         
33951         for (var i = 0; i < this.cols; i++){
33952             maxY[i] = pos.y;
33953         }
33954         
33955         Roo.each(queue, function(box, k){
33956             
33957             var col = k % this.cols;
33958             
33959             Roo.each(box, function(b,kk){
33960                 
33961                 b.el.position('absolute');
33962                 
33963                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33964                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33965                 
33966                 if(b.size == 'md-left' || b.size == 'md-right'){
33967                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33968                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33969                 }
33970                 
33971                 b.el.setWidth(width);
33972                 b.el.setHeight(height);
33973                 // iframe?
33974                 b.el.select('iframe',true).setSize(width,height);
33975                 
33976             }, this);
33977             
33978             for (var i = 0; i < this.cols; i++){
33979                 
33980                 if(maxY[i] < maxY[col]){
33981                     col = i;
33982                     continue;
33983                 }
33984                 
33985                 col = Math.min(col, i);
33986                 
33987             }
33988             
33989             x = pos.x + col * (this.colWidth + this.padWidth);
33990             
33991             y = maxY[col];
33992             
33993             var positions = [];
33994             
33995             switch (box.length){
33996                 case 1 :
33997                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33998                     break;
33999                 case 2 :
34000                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34001                     break;
34002                 case 3 :
34003                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34004                     break;
34005                 case 4 :
34006                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34007                     break;
34008                 default :
34009                     break;
34010             }
34011             
34012             Roo.each(box, function(b,kk){
34013                 
34014                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34015                 
34016                 var sz = b.el.getSize();
34017                 
34018                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34019                 
34020             }, this);
34021             
34022         }, this);
34023         
34024         var mY = 0;
34025         
34026         for (var i = 0; i < this.cols; i++){
34027             mY = Math.max(mY, maxY[i]);
34028         }
34029         
34030         this.el.setHeight(mY - pos.y);
34031         
34032     },
34033     
34034 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34035 //    {
34036 //        var pos = this.el.getBox(true);
34037 //        var x = pos.x;
34038 //        var y = pos.y;
34039 //        var maxX = pos.right;
34040 //        
34041 //        var maxHeight = 0;
34042 //        
34043 //        Roo.each(items, function(item, k){
34044 //            
34045 //            var c = k % 2;
34046 //            
34047 //            item.el.position('absolute');
34048 //                
34049 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34050 //
34051 //            item.el.setWidth(width);
34052 //
34053 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34054 //
34055 //            item.el.setHeight(height);
34056 //            
34057 //            if(c == 0){
34058 //                item.el.setXY([x, y], isInstant ? false : true);
34059 //            } else {
34060 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34061 //            }
34062 //            
34063 //            y = y + height + this.alternativePadWidth;
34064 //            
34065 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34066 //            
34067 //        }, this);
34068 //        
34069 //        this.el.setHeight(maxHeight);
34070 //        
34071 //    },
34072     
34073     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34074     {
34075         var pos = this.el.getBox(true);
34076         
34077         var minX = pos.x;
34078         var minY = pos.y;
34079         
34080         var maxX = pos.right;
34081         
34082         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34083         
34084         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34085         
34086         Roo.each(queue, function(box, k){
34087             
34088             Roo.each(box, function(b, kk){
34089                 
34090                 b.el.position('absolute');
34091                 
34092                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34093                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34094                 
34095                 if(b.size == 'md-left' || b.size == 'md-right'){
34096                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34097                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34098                 }
34099                 
34100                 b.el.setWidth(width);
34101                 b.el.setHeight(height);
34102                 
34103             }, this);
34104             
34105             if(!box.length){
34106                 return;
34107             }
34108             
34109             var positions = [];
34110             
34111             switch (box.length){
34112                 case 1 :
34113                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34114                     break;
34115                 case 2 :
34116                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34117                     break;
34118                 case 3 :
34119                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34120                     break;
34121                 case 4 :
34122                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34123                     break;
34124                 default :
34125                     break;
34126             }
34127             
34128             Roo.each(box, function(b,kk){
34129                 
34130                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34131                 
34132                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34133                 
34134             }, this);
34135             
34136         }, this);
34137         
34138     },
34139     
34140     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34141     {
34142         Roo.each(eItems, function(b,k){
34143             
34144             b.size = (k == 0) ? 'sm' : 'xs';
34145             b.x = (k == 0) ? 2 : 1;
34146             b.y = (k == 0) ? 2 : 1;
34147             
34148             b.el.position('absolute');
34149             
34150             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34151                 
34152             b.el.setWidth(width);
34153             
34154             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34155             
34156             b.el.setHeight(height);
34157             
34158         }, this);
34159
34160         var positions = [];
34161         
34162         positions.push({
34163             x : maxX - this.unitWidth * 2 - this.gutter,
34164             y : minY
34165         });
34166         
34167         positions.push({
34168             x : maxX - this.unitWidth,
34169             y : minY + (this.unitWidth + this.gutter) * 2
34170         });
34171         
34172         positions.push({
34173             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34174             y : minY
34175         });
34176         
34177         Roo.each(eItems, function(b,k){
34178             
34179             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34180
34181         }, this);
34182         
34183     },
34184     
34185     getVerticalOneBoxColPositions : function(x, y, box)
34186     {
34187         var pos = [];
34188         
34189         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34190         
34191         if(box[0].size == 'md-left'){
34192             rand = 0;
34193         }
34194         
34195         if(box[0].size == 'md-right'){
34196             rand = 1;
34197         }
34198         
34199         pos.push({
34200             x : x + (this.unitWidth + this.gutter) * rand,
34201             y : y
34202         });
34203         
34204         return pos;
34205     },
34206     
34207     getVerticalTwoBoxColPositions : function(x, y, box)
34208     {
34209         var pos = [];
34210         
34211         if(box[0].size == 'xs'){
34212             
34213             pos.push({
34214                 x : x,
34215                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34216             });
34217
34218             pos.push({
34219                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34220                 y : y
34221             });
34222             
34223             return pos;
34224             
34225         }
34226         
34227         pos.push({
34228             x : x,
34229             y : y
34230         });
34231
34232         pos.push({
34233             x : x + (this.unitWidth + this.gutter) * 2,
34234             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34235         });
34236         
34237         return pos;
34238         
34239     },
34240     
34241     getVerticalThreeBoxColPositions : function(x, y, box)
34242     {
34243         var pos = [];
34244         
34245         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34246             
34247             pos.push({
34248                 x : x,
34249                 y : y
34250             });
34251
34252             pos.push({
34253                 x : x + (this.unitWidth + this.gutter) * 1,
34254                 y : y
34255             });
34256             
34257             pos.push({
34258                 x : x + (this.unitWidth + this.gutter) * 2,
34259                 y : y
34260             });
34261             
34262             return pos;
34263             
34264         }
34265         
34266         if(box[0].size == 'xs' && box[1].size == 'xs'){
34267             
34268             pos.push({
34269                 x : x,
34270                 y : y
34271             });
34272
34273             pos.push({
34274                 x : x,
34275                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34276             });
34277             
34278             pos.push({
34279                 x : x + (this.unitWidth + this.gutter) * 1,
34280                 y : y
34281             });
34282             
34283             return pos;
34284             
34285         }
34286         
34287         pos.push({
34288             x : x,
34289             y : y
34290         });
34291
34292         pos.push({
34293             x : x + (this.unitWidth + this.gutter) * 2,
34294             y : y
34295         });
34296
34297         pos.push({
34298             x : x + (this.unitWidth + this.gutter) * 2,
34299             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34300         });
34301             
34302         return pos;
34303         
34304     },
34305     
34306     getVerticalFourBoxColPositions : function(x, y, box)
34307     {
34308         var pos = [];
34309         
34310         if(box[0].size == 'xs'){
34311             
34312             pos.push({
34313                 x : x,
34314                 y : y
34315             });
34316
34317             pos.push({
34318                 x : x,
34319                 y : y + (this.unitHeight + this.gutter) * 1
34320             });
34321             
34322             pos.push({
34323                 x : x,
34324                 y : y + (this.unitHeight + this.gutter) * 2
34325             });
34326             
34327             pos.push({
34328                 x : x + (this.unitWidth + this.gutter) * 1,
34329                 y : y
34330             });
34331             
34332             return pos;
34333             
34334         }
34335         
34336         pos.push({
34337             x : x,
34338             y : y
34339         });
34340
34341         pos.push({
34342             x : x + (this.unitWidth + this.gutter) * 2,
34343             y : y
34344         });
34345
34346         pos.push({
34347             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34348             y : y + (this.unitHeight + this.gutter) * 1
34349         });
34350
34351         pos.push({
34352             x : x + (this.unitWidth + this.gutter) * 2,
34353             y : y + (this.unitWidth + this.gutter) * 2
34354         });
34355
34356         return pos;
34357         
34358     },
34359     
34360     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34361     {
34362         var pos = [];
34363         
34364         if(box[0].size == 'md-left'){
34365             pos.push({
34366                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34367                 y : minY
34368             });
34369             
34370             return pos;
34371         }
34372         
34373         if(box[0].size == 'md-right'){
34374             pos.push({
34375                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34376                 y : minY + (this.unitWidth + this.gutter) * 1
34377             });
34378             
34379             return pos;
34380         }
34381         
34382         var rand = Math.floor(Math.random() * (4 - box[0].y));
34383         
34384         pos.push({
34385             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34386             y : minY + (this.unitWidth + this.gutter) * rand
34387         });
34388         
34389         return pos;
34390         
34391     },
34392     
34393     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34394     {
34395         var pos = [];
34396         
34397         if(box[0].size == 'xs'){
34398             
34399             pos.push({
34400                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34401                 y : minY
34402             });
34403
34404             pos.push({
34405                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34406                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34407             });
34408             
34409             return pos;
34410             
34411         }
34412         
34413         pos.push({
34414             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34415             y : minY
34416         });
34417
34418         pos.push({
34419             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34420             y : minY + (this.unitWidth + this.gutter) * 2
34421         });
34422         
34423         return pos;
34424         
34425     },
34426     
34427     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34428     {
34429         var pos = [];
34430         
34431         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34432             
34433             pos.push({
34434                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34435                 y : minY
34436             });
34437
34438             pos.push({
34439                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34440                 y : minY + (this.unitWidth + this.gutter) * 1
34441             });
34442             
34443             pos.push({
34444                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34445                 y : minY + (this.unitWidth + this.gutter) * 2
34446             });
34447             
34448             return pos;
34449             
34450         }
34451         
34452         if(box[0].size == 'xs' && box[1].size == 'xs'){
34453             
34454             pos.push({
34455                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34456                 y : minY
34457             });
34458
34459             pos.push({
34460                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34461                 y : minY
34462             });
34463             
34464             pos.push({
34465                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34466                 y : minY + (this.unitWidth + this.gutter) * 1
34467             });
34468             
34469             return pos;
34470             
34471         }
34472         
34473         pos.push({
34474             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34475             y : minY
34476         });
34477
34478         pos.push({
34479             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34480             y : minY + (this.unitWidth + this.gutter) * 2
34481         });
34482
34483         pos.push({
34484             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34485             y : minY + (this.unitWidth + this.gutter) * 2
34486         });
34487             
34488         return pos;
34489         
34490     },
34491     
34492     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34493     {
34494         var pos = [];
34495         
34496         if(box[0].size == 'xs'){
34497             
34498             pos.push({
34499                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34500                 y : minY
34501             });
34502
34503             pos.push({
34504                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34505                 y : minY
34506             });
34507             
34508             pos.push({
34509                 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),
34510                 y : minY
34511             });
34512             
34513             pos.push({
34514                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34515                 y : minY + (this.unitWidth + this.gutter) * 1
34516             });
34517             
34518             return pos;
34519             
34520         }
34521         
34522         pos.push({
34523             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34524             y : minY
34525         });
34526         
34527         pos.push({
34528             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34529             y : minY + (this.unitWidth + this.gutter) * 2
34530         });
34531         
34532         pos.push({
34533             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34534             y : minY + (this.unitWidth + this.gutter) * 2
34535         });
34536         
34537         pos.push({
34538             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),
34539             y : minY + (this.unitWidth + this.gutter) * 2
34540         });
34541
34542         return pos;
34543         
34544     },
34545     
34546     /**
34547     * remove a Masonry Brick
34548     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34549     */
34550     removeBrick : function(brick_id)
34551     {
34552         if (!brick_id) {
34553             return;
34554         }
34555         
34556         for (var i = 0; i<this.bricks.length; i++) {
34557             if (this.bricks[i].id == brick_id) {
34558                 this.bricks.splice(i,1);
34559                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34560                 this.initial();
34561             }
34562         }
34563     },
34564     
34565     /**
34566     * adds a Masonry Brick
34567     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34568     */
34569     addBrick : function(cfg)
34570     {
34571         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34572         //this.register(cn);
34573         cn.parentId = this.id;
34574         cn.render(this.el);
34575         return cn;
34576     },
34577     
34578     /**
34579     * register a Masonry Brick
34580     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34581     */
34582     
34583     register : function(brick)
34584     {
34585         this.bricks.push(brick);
34586         brick.masonryId = this.id;
34587     },
34588     
34589     /**
34590     * clear all the Masonry Brick
34591     */
34592     clearAll : function()
34593     {
34594         this.bricks = [];
34595         //this.getChildContainer().dom.innerHTML = "";
34596         this.el.dom.innerHTML = '';
34597     },
34598     
34599     getSelected : function()
34600     {
34601         if (!this.selectedBrick) {
34602             return false;
34603         }
34604         
34605         return this.selectedBrick;
34606     }
34607 });
34608
34609 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34610     
34611     groups: {},
34612      /**
34613     * register a Masonry Layout
34614     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34615     */
34616     
34617     register : function(layout)
34618     {
34619         this.groups[layout.id] = layout;
34620     },
34621     /**
34622     * fetch a  Masonry Layout based on the masonry layout ID
34623     * @param {string} the masonry layout to add
34624     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34625     */
34626     
34627     get: function(layout_id) {
34628         if (typeof(this.groups[layout_id]) == 'undefined') {
34629             return false;
34630         }
34631         return this.groups[layout_id] ;
34632     }
34633     
34634     
34635     
34636 });
34637
34638  
34639
34640  /**
34641  *
34642  * This is based on 
34643  * http://masonry.desandro.com
34644  *
34645  * The idea is to render all the bricks based on vertical width...
34646  *
34647  * The original code extends 'outlayer' - we might need to use that....
34648  * 
34649  */
34650
34651
34652 /**
34653  * @class Roo.bootstrap.LayoutMasonryAuto
34654  * @extends Roo.bootstrap.Component
34655  * Bootstrap Layout Masonry class
34656  * 
34657  * @constructor
34658  * Create a new Element
34659  * @param {Object} config The config object
34660  */
34661
34662 Roo.bootstrap.LayoutMasonryAuto = function(config){
34663     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34664 };
34665
34666 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34667     
34668       /**
34669      * @cfg {Boolean} isFitWidth  - resize the width..
34670      */   
34671     isFitWidth : false,  // options..
34672     /**
34673      * @cfg {Boolean} isOriginLeft = left align?
34674      */   
34675     isOriginLeft : true,
34676     /**
34677      * @cfg {Boolean} isOriginTop = top align?
34678      */   
34679     isOriginTop : false,
34680     /**
34681      * @cfg {Boolean} isLayoutInstant = no animation?
34682      */   
34683     isLayoutInstant : false, // needed?
34684     /**
34685      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34686      */   
34687     isResizingContainer : true,
34688     /**
34689      * @cfg {Number} columnWidth  width of the columns 
34690      */   
34691     
34692     columnWidth : 0,
34693     
34694     /**
34695      * @cfg {Number} maxCols maximum number of columns
34696      */   
34697     
34698     maxCols: 0,
34699     /**
34700      * @cfg {Number} padHeight padding below box..
34701      */   
34702     
34703     padHeight : 10, 
34704     
34705     /**
34706      * @cfg {Boolean} isAutoInitial defalut true
34707      */   
34708     
34709     isAutoInitial : true, 
34710     
34711     // private?
34712     gutter : 0,
34713     
34714     containerWidth: 0,
34715     initialColumnWidth : 0,
34716     currentSize : null,
34717     
34718     colYs : null, // array.
34719     maxY : 0,
34720     padWidth: 10,
34721     
34722     
34723     tag: 'div',
34724     cls: '',
34725     bricks: null, //CompositeElement
34726     cols : 0, // array?
34727     // element : null, // wrapped now this.el
34728     _isLayoutInited : null, 
34729     
34730     
34731     getAutoCreate : function(){
34732         
34733         var cfg = {
34734             tag: this.tag,
34735             cls: 'blog-masonary-wrapper ' + this.cls,
34736             cn : {
34737                 cls : 'mas-boxes masonary'
34738             }
34739         };
34740         
34741         return cfg;
34742     },
34743     
34744     getChildContainer: function( )
34745     {
34746         if (this.boxesEl) {
34747             return this.boxesEl;
34748         }
34749         
34750         this.boxesEl = this.el.select('.mas-boxes').first();
34751         
34752         return this.boxesEl;
34753     },
34754     
34755     
34756     initEvents : function()
34757     {
34758         var _this = this;
34759         
34760         if(this.isAutoInitial){
34761             Roo.log('hook children rendered');
34762             this.on('childrenrendered', function() {
34763                 Roo.log('children rendered');
34764                 _this.initial();
34765             } ,this);
34766         }
34767         
34768     },
34769     
34770     initial : function()
34771     {
34772         this.reloadItems();
34773
34774         this.currentSize = this.el.getBox(true);
34775
34776         /// was window resize... - let's see if this works..
34777         Roo.EventManager.onWindowResize(this.resize, this); 
34778
34779         if(!this.isAutoInitial){
34780             this.layout();
34781             return;
34782         }
34783         
34784         this.layout.defer(500,this);
34785     },
34786     
34787     reloadItems: function()
34788     {
34789         this.bricks = this.el.select('.masonry-brick', true);
34790         
34791         this.bricks.each(function(b) {
34792             //Roo.log(b.getSize());
34793             if (!b.attr('originalwidth')) {
34794                 b.attr('originalwidth',  b.getSize().width);
34795             }
34796             
34797         });
34798         
34799         Roo.log(this.bricks.elements.length);
34800     },
34801     
34802     resize : function()
34803     {
34804         Roo.log('resize');
34805         var cs = this.el.getBox(true);
34806         
34807         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34808             Roo.log("no change in with or X");
34809             return;
34810         }
34811         this.currentSize = cs;
34812         this.layout();
34813     },
34814     
34815     layout : function()
34816     {
34817          Roo.log('layout');
34818         this._resetLayout();
34819         //this._manageStamps();
34820       
34821         // don't animate first layout
34822         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34823         this.layoutItems( isInstant );
34824       
34825         // flag for initalized
34826         this._isLayoutInited = true;
34827     },
34828     
34829     layoutItems : function( isInstant )
34830     {
34831         //var items = this._getItemsForLayout( this.items );
34832         // original code supports filtering layout items.. we just ignore it..
34833         
34834         this._layoutItems( this.bricks , isInstant );
34835       
34836         this._postLayout();
34837     },
34838     _layoutItems : function ( items , isInstant)
34839     {
34840        //this.fireEvent( 'layout', this, items );
34841     
34842
34843         if ( !items || !items.elements.length ) {
34844           // no items, emit event with empty array
34845             return;
34846         }
34847
34848         var queue = [];
34849         items.each(function(item) {
34850             Roo.log("layout item");
34851             Roo.log(item);
34852             // get x/y object from method
34853             var position = this._getItemLayoutPosition( item );
34854             // enqueue
34855             position.item = item;
34856             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34857             queue.push( position );
34858         }, this);
34859       
34860         this._processLayoutQueue( queue );
34861     },
34862     /** Sets position of item in DOM
34863     * @param {Element} item
34864     * @param {Number} x - horizontal position
34865     * @param {Number} y - vertical position
34866     * @param {Boolean} isInstant - disables transitions
34867     */
34868     _processLayoutQueue : function( queue )
34869     {
34870         for ( var i=0, len = queue.length; i < len; i++ ) {
34871             var obj = queue[i];
34872             obj.item.position('absolute');
34873             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34874         }
34875     },
34876       
34877     
34878     /**
34879     * Any logic you want to do after each layout,
34880     * i.e. size the container
34881     */
34882     _postLayout : function()
34883     {
34884         this.resizeContainer();
34885     },
34886     
34887     resizeContainer : function()
34888     {
34889         if ( !this.isResizingContainer ) {
34890             return;
34891         }
34892         var size = this._getContainerSize();
34893         if ( size ) {
34894             this.el.setSize(size.width,size.height);
34895             this.boxesEl.setSize(size.width,size.height);
34896         }
34897     },
34898     
34899     
34900     
34901     _resetLayout : function()
34902     {
34903         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34904         this.colWidth = this.el.getWidth();
34905         //this.gutter = this.el.getWidth(); 
34906         
34907         this.measureColumns();
34908
34909         // reset column Y
34910         var i = this.cols;
34911         this.colYs = [];
34912         while (i--) {
34913             this.colYs.push( 0 );
34914         }
34915     
34916         this.maxY = 0;
34917     },
34918
34919     measureColumns : function()
34920     {
34921         this.getContainerWidth();
34922       // if columnWidth is 0, default to outerWidth of first item
34923         if ( !this.columnWidth ) {
34924             var firstItem = this.bricks.first();
34925             Roo.log(firstItem);
34926             this.columnWidth  = this.containerWidth;
34927             if (firstItem && firstItem.attr('originalwidth') ) {
34928                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34929             }
34930             // columnWidth fall back to item of first element
34931             Roo.log("set column width?");
34932                         this.initialColumnWidth = this.columnWidth  ;
34933
34934             // if first elem has no width, default to size of container
34935             
34936         }
34937         
34938         
34939         if (this.initialColumnWidth) {
34940             this.columnWidth = this.initialColumnWidth;
34941         }
34942         
34943         
34944             
34945         // column width is fixed at the top - however if container width get's smaller we should
34946         // reduce it...
34947         
34948         // this bit calcs how man columns..
34949             
34950         var columnWidth = this.columnWidth += this.gutter;
34951       
34952         // calculate columns
34953         var containerWidth = this.containerWidth + this.gutter;
34954         
34955         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34956         // fix rounding errors, typically with gutters
34957         var excess = columnWidth - containerWidth % columnWidth;
34958         
34959         
34960         // if overshoot is less than a pixel, round up, otherwise floor it
34961         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34962         cols = Math[ mathMethod ]( cols );
34963         this.cols = Math.max( cols, 1 );
34964         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34965         
34966          // padding positioning..
34967         var totalColWidth = this.cols * this.columnWidth;
34968         var padavail = this.containerWidth - totalColWidth;
34969         // so for 2 columns - we need 3 'pads'
34970         
34971         var padNeeded = (1+this.cols) * this.padWidth;
34972         
34973         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34974         
34975         this.columnWidth += padExtra
34976         //this.padWidth = Math.floor(padavail /  ( this.cols));
34977         
34978         // adjust colum width so that padding is fixed??
34979         
34980         // we have 3 columns ... total = width * 3
34981         // we have X left over... that should be used by 
34982         
34983         //if (this.expandC) {
34984             
34985         //}
34986         
34987         
34988         
34989     },
34990     
34991     getContainerWidth : function()
34992     {
34993        /* // container is parent if fit width
34994         var container = this.isFitWidth ? this.element.parentNode : this.element;
34995         // check that this.size and size are there
34996         // IE8 triggers resize on body size change, so they might not be
34997         
34998         var size = getSize( container );  //FIXME
34999         this.containerWidth = size && size.innerWidth; //FIXME
35000         */
35001          
35002         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35003         
35004     },
35005     
35006     _getItemLayoutPosition : function( item )  // what is item?
35007     {
35008         // we resize the item to our columnWidth..
35009       
35010         item.setWidth(this.columnWidth);
35011         item.autoBoxAdjust  = false;
35012         
35013         var sz = item.getSize();
35014  
35015         // how many columns does this brick span
35016         var remainder = this.containerWidth % this.columnWidth;
35017         
35018         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35019         // round if off by 1 pixel, otherwise use ceil
35020         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35021         colSpan = Math.min( colSpan, this.cols );
35022         
35023         // normally this should be '1' as we dont' currently allow multi width columns..
35024         
35025         var colGroup = this._getColGroup( colSpan );
35026         // get the minimum Y value from the columns
35027         var minimumY = Math.min.apply( Math, colGroup );
35028         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35029         
35030         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35031          
35032         // position the brick
35033         var position = {
35034             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35035             y: this.currentSize.y + minimumY + this.padHeight
35036         };
35037         
35038         Roo.log(position);
35039         // apply setHeight to necessary columns
35040         var setHeight = minimumY + sz.height + this.padHeight;
35041         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35042         
35043         var setSpan = this.cols + 1 - colGroup.length;
35044         for ( var i = 0; i < setSpan; i++ ) {
35045           this.colYs[ shortColIndex + i ] = setHeight ;
35046         }
35047       
35048         return position;
35049     },
35050     
35051     /**
35052      * @param {Number} colSpan - number of columns the element spans
35053      * @returns {Array} colGroup
35054      */
35055     _getColGroup : function( colSpan )
35056     {
35057         if ( colSpan < 2 ) {
35058           // if brick spans only one column, use all the column Ys
35059           return this.colYs;
35060         }
35061       
35062         var colGroup = [];
35063         // how many different places could this brick fit horizontally
35064         var groupCount = this.cols + 1 - colSpan;
35065         // for each group potential horizontal position
35066         for ( var i = 0; i < groupCount; i++ ) {
35067           // make an array of colY values for that one group
35068           var groupColYs = this.colYs.slice( i, i + colSpan );
35069           // and get the max value of the array
35070           colGroup[i] = Math.max.apply( Math, groupColYs );
35071         }
35072         return colGroup;
35073     },
35074     /*
35075     _manageStamp : function( stamp )
35076     {
35077         var stampSize =  stamp.getSize();
35078         var offset = stamp.getBox();
35079         // get the columns that this stamp affects
35080         var firstX = this.isOriginLeft ? offset.x : offset.right;
35081         var lastX = firstX + stampSize.width;
35082         var firstCol = Math.floor( firstX / this.columnWidth );
35083         firstCol = Math.max( 0, firstCol );
35084         
35085         var lastCol = Math.floor( lastX / this.columnWidth );
35086         // lastCol should not go over if multiple of columnWidth #425
35087         lastCol -= lastX % this.columnWidth ? 0 : 1;
35088         lastCol = Math.min( this.cols - 1, lastCol );
35089         
35090         // set colYs to bottom of the stamp
35091         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35092             stampSize.height;
35093             
35094         for ( var i = firstCol; i <= lastCol; i++ ) {
35095           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35096         }
35097     },
35098     */
35099     
35100     _getContainerSize : function()
35101     {
35102         this.maxY = Math.max.apply( Math, this.colYs );
35103         var size = {
35104             height: this.maxY
35105         };
35106       
35107         if ( this.isFitWidth ) {
35108             size.width = this._getContainerFitWidth();
35109         }
35110       
35111         return size;
35112     },
35113     
35114     _getContainerFitWidth : function()
35115     {
35116         var unusedCols = 0;
35117         // count unused columns
35118         var i = this.cols;
35119         while ( --i ) {
35120           if ( this.colYs[i] !== 0 ) {
35121             break;
35122           }
35123           unusedCols++;
35124         }
35125         // fit container to columns that have been used
35126         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35127     },
35128     
35129     needsResizeLayout : function()
35130     {
35131         var previousWidth = this.containerWidth;
35132         this.getContainerWidth();
35133         return previousWidth !== this.containerWidth;
35134     }
35135  
35136 });
35137
35138  
35139
35140  /*
35141  * - LGPL
35142  *
35143  * element
35144  * 
35145  */
35146
35147 /**
35148  * @class Roo.bootstrap.MasonryBrick
35149  * @extends Roo.bootstrap.Component
35150  * Bootstrap MasonryBrick class
35151  * 
35152  * @constructor
35153  * Create a new MasonryBrick
35154  * @param {Object} config The config object
35155  */
35156
35157 Roo.bootstrap.MasonryBrick = function(config){
35158     
35159     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35160     
35161     Roo.bootstrap.MasonryBrick.register(this);
35162     
35163     this.addEvents({
35164         // raw events
35165         /**
35166          * @event click
35167          * When a MasonryBrick is clcik
35168          * @param {Roo.bootstrap.MasonryBrick} this
35169          * @param {Roo.EventObject} e
35170          */
35171         "click" : true
35172     });
35173 };
35174
35175 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35176     
35177     /**
35178      * @cfg {String} title
35179      */   
35180     title : '',
35181     /**
35182      * @cfg {String} html
35183      */   
35184     html : '',
35185     /**
35186      * @cfg {String} bgimage
35187      */   
35188     bgimage : '',
35189     /**
35190      * @cfg {String} videourl
35191      */   
35192     videourl : '',
35193     /**
35194      * @cfg {String} cls
35195      */   
35196     cls : '',
35197     /**
35198      * @cfg {String} href
35199      */   
35200     href : '',
35201     /**
35202      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35203      */   
35204     size : 'xs',
35205     
35206     /**
35207      * @cfg {String} placetitle (center|bottom)
35208      */   
35209     placetitle : '',
35210     
35211     /**
35212      * @cfg {Boolean} isFitContainer defalut true
35213      */   
35214     isFitContainer : true, 
35215     
35216     /**
35217      * @cfg {Boolean} preventDefault defalut false
35218      */   
35219     preventDefault : false, 
35220     
35221     /**
35222      * @cfg {Boolean} inverse defalut false
35223      */   
35224     maskInverse : false, 
35225     
35226     getAutoCreate : function()
35227     {
35228         if(!this.isFitContainer){
35229             return this.getSplitAutoCreate();
35230         }
35231         
35232         var cls = 'masonry-brick masonry-brick-full';
35233         
35234         if(this.href.length){
35235             cls += ' masonry-brick-link';
35236         }
35237         
35238         if(this.bgimage.length){
35239             cls += ' masonry-brick-image';
35240         }
35241         
35242         if(this.maskInverse){
35243             cls += ' mask-inverse';
35244         }
35245         
35246         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35247             cls += ' enable-mask';
35248         }
35249         
35250         if(this.size){
35251             cls += ' masonry-' + this.size + '-brick';
35252         }
35253         
35254         if(this.placetitle.length){
35255             
35256             switch (this.placetitle) {
35257                 case 'center' :
35258                     cls += ' masonry-center-title';
35259                     break;
35260                 case 'bottom' :
35261                     cls += ' masonry-bottom-title';
35262                     break;
35263                 default:
35264                     break;
35265             }
35266             
35267         } else {
35268             if(!this.html.length && !this.bgimage.length){
35269                 cls += ' masonry-center-title';
35270             }
35271
35272             if(!this.html.length && this.bgimage.length){
35273                 cls += ' masonry-bottom-title';
35274             }
35275         }
35276         
35277         if(this.cls){
35278             cls += ' ' + this.cls;
35279         }
35280         
35281         var cfg = {
35282             tag: (this.href.length) ? 'a' : 'div',
35283             cls: cls,
35284             cn: [
35285                 {
35286                     tag: 'div',
35287                     cls: 'masonry-brick-mask'
35288                 },
35289                 {
35290                     tag: 'div',
35291                     cls: 'masonry-brick-paragraph',
35292                     cn: []
35293                 }
35294             ]
35295         };
35296         
35297         if(this.href.length){
35298             cfg.href = this.href;
35299         }
35300         
35301         var cn = cfg.cn[1].cn;
35302         
35303         if(this.title.length){
35304             cn.push({
35305                 tag: 'h4',
35306                 cls: 'masonry-brick-title',
35307                 html: this.title
35308             });
35309         }
35310         
35311         if(this.html.length){
35312             cn.push({
35313                 tag: 'p',
35314                 cls: 'masonry-brick-text',
35315                 html: this.html
35316             });
35317         }
35318         
35319         if (!this.title.length && !this.html.length) {
35320             cfg.cn[1].cls += ' hide';
35321         }
35322         
35323         if(this.bgimage.length){
35324             cfg.cn.push({
35325                 tag: 'img',
35326                 cls: 'masonry-brick-image-view',
35327                 src: this.bgimage
35328             });
35329         }
35330         
35331         if(this.videourl.length){
35332             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35333             // youtube support only?
35334             cfg.cn.push({
35335                 tag: 'iframe',
35336                 cls: 'masonry-brick-image-view',
35337                 src: vurl,
35338                 frameborder : 0,
35339                 allowfullscreen : true
35340             });
35341         }
35342         
35343         return cfg;
35344         
35345     },
35346     
35347     getSplitAutoCreate : function()
35348     {
35349         var cls = 'masonry-brick masonry-brick-split';
35350         
35351         if(this.href.length){
35352             cls += ' masonry-brick-link';
35353         }
35354         
35355         if(this.bgimage.length){
35356             cls += ' masonry-brick-image';
35357         }
35358         
35359         if(this.size){
35360             cls += ' masonry-' + this.size + '-brick';
35361         }
35362         
35363         switch (this.placetitle) {
35364             case 'center' :
35365                 cls += ' masonry-center-title';
35366                 break;
35367             case 'bottom' :
35368                 cls += ' masonry-bottom-title';
35369                 break;
35370             default:
35371                 if(!this.bgimage.length){
35372                     cls += ' masonry-center-title';
35373                 }
35374
35375                 if(this.bgimage.length){
35376                     cls += ' masonry-bottom-title';
35377                 }
35378                 break;
35379         }
35380         
35381         if(this.cls){
35382             cls += ' ' + this.cls;
35383         }
35384         
35385         var cfg = {
35386             tag: (this.href.length) ? 'a' : 'div',
35387             cls: cls,
35388             cn: [
35389                 {
35390                     tag: 'div',
35391                     cls: 'masonry-brick-split-head',
35392                     cn: [
35393                         {
35394                             tag: 'div',
35395                             cls: 'masonry-brick-paragraph',
35396                             cn: []
35397                         }
35398                     ]
35399                 },
35400                 {
35401                     tag: 'div',
35402                     cls: 'masonry-brick-split-body',
35403                     cn: []
35404                 }
35405             ]
35406         };
35407         
35408         if(this.href.length){
35409             cfg.href = this.href;
35410         }
35411         
35412         if(this.title.length){
35413             cfg.cn[0].cn[0].cn.push({
35414                 tag: 'h4',
35415                 cls: 'masonry-brick-title',
35416                 html: this.title
35417             });
35418         }
35419         
35420         if(this.html.length){
35421             cfg.cn[1].cn.push({
35422                 tag: 'p',
35423                 cls: 'masonry-brick-text',
35424                 html: this.html
35425             });
35426         }
35427
35428         if(this.bgimage.length){
35429             cfg.cn[0].cn.push({
35430                 tag: 'img',
35431                 cls: 'masonry-brick-image-view',
35432                 src: this.bgimage
35433             });
35434         }
35435         
35436         if(this.videourl.length){
35437             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35438             // youtube support only?
35439             cfg.cn[0].cn.cn.push({
35440                 tag: 'iframe',
35441                 cls: 'masonry-brick-image-view',
35442                 src: vurl,
35443                 frameborder : 0,
35444                 allowfullscreen : true
35445             });
35446         }
35447         
35448         return cfg;
35449     },
35450     
35451     initEvents: function() 
35452     {
35453         switch (this.size) {
35454             case 'xs' :
35455                 this.x = 1;
35456                 this.y = 1;
35457                 break;
35458             case 'sm' :
35459                 this.x = 2;
35460                 this.y = 2;
35461                 break;
35462             case 'md' :
35463             case 'md-left' :
35464             case 'md-right' :
35465                 this.x = 3;
35466                 this.y = 3;
35467                 break;
35468             case 'tall' :
35469                 this.x = 2;
35470                 this.y = 3;
35471                 break;
35472             case 'wide' :
35473                 this.x = 3;
35474                 this.y = 2;
35475                 break;
35476             case 'wide-thin' :
35477                 this.x = 3;
35478                 this.y = 1;
35479                 break;
35480                         
35481             default :
35482                 break;
35483         }
35484         
35485         if(Roo.isTouch){
35486             this.el.on('touchstart', this.onTouchStart, this);
35487             this.el.on('touchmove', this.onTouchMove, this);
35488             this.el.on('touchend', this.onTouchEnd, this);
35489             this.el.on('contextmenu', this.onContextMenu, this);
35490         } else {
35491             this.el.on('mouseenter'  ,this.enter, this);
35492             this.el.on('mouseleave', this.leave, this);
35493             this.el.on('click', this.onClick, this);
35494         }
35495         
35496         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35497             this.parent().bricks.push(this);   
35498         }
35499         
35500     },
35501     
35502     onClick: function(e, el)
35503     {
35504         var time = this.endTimer - this.startTimer;
35505         // Roo.log(e.preventDefault());
35506         if(Roo.isTouch){
35507             if(time > 1000){
35508                 e.preventDefault();
35509                 return;
35510             }
35511         }
35512         
35513         if(!this.preventDefault){
35514             return;
35515         }
35516         
35517         e.preventDefault();
35518         
35519         if (this.activeClass != '') {
35520             this.selectBrick();
35521         }
35522         
35523         this.fireEvent('click', this, e);
35524     },
35525     
35526     enter: 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.9, true);
35536         }
35537     },
35538     
35539     leave: function(e, el)
35540     {
35541         e.preventDefault();
35542         
35543         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35544             return;
35545         }
35546         
35547         if(this.bgimage.length && this.html.length){
35548             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35549         }
35550     },
35551     
35552     onTouchStart: function(e, el)
35553     {
35554 //        e.preventDefault();
35555         
35556         this.touchmoved = false;
35557         
35558         if(!this.isFitContainer){
35559             return;
35560         }
35561         
35562         if(!this.bgimage.length || !this.html.length){
35563             return;
35564         }
35565         
35566         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35567         
35568         this.timer = new Date().getTime();
35569         
35570     },
35571     
35572     onTouchMove: function(e, el)
35573     {
35574         this.touchmoved = true;
35575     },
35576     
35577     onContextMenu : function(e,el)
35578     {
35579         e.preventDefault();
35580         e.stopPropagation();
35581         return false;
35582     },
35583     
35584     onTouchEnd: function(e, el)
35585     {
35586 //        e.preventDefault();
35587         
35588         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35589         
35590             this.leave(e,el);
35591             
35592             return;
35593         }
35594         
35595         if(!this.bgimage.length || !this.html.length){
35596             
35597             if(this.href.length){
35598                 window.location.href = this.href;
35599             }
35600             
35601             return;
35602         }
35603         
35604         if(!this.isFitContainer){
35605             return;
35606         }
35607         
35608         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35609         
35610         window.location.href = this.href;
35611     },
35612     
35613     //selection on single brick only
35614     selectBrick : function() {
35615         
35616         if (!this.parentId) {
35617             return;
35618         }
35619         
35620         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35621         var index = m.selectedBrick.indexOf(this.id);
35622         
35623         if ( index > -1) {
35624             m.selectedBrick.splice(index,1);
35625             this.el.removeClass(this.activeClass);
35626             return;
35627         }
35628         
35629         for(var i = 0; i < m.selectedBrick.length; i++) {
35630             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35631             b.el.removeClass(b.activeClass);
35632         }
35633         
35634         m.selectedBrick = [];
35635         
35636         m.selectedBrick.push(this.id);
35637         this.el.addClass(this.activeClass);
35638         return;
35639     },
35640     
35641     isSelected : function(){
35642         return this.el.hasClass(this.activeClass);
35643         
35644     }
35645 });
35646
35647 Roo.apply(Roo.bootstrap.MasonryBrick, {
35648     
35649     //groups: {},
35650     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35651      /**
35652     * register a Masonry Brick
35653     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35654     */
35655     
35656     register : function(brick)
35657     {
35658         //this.groups[brick.id] = brick;
35659         this.groups.add(brick.id, brick);
35660     },
35661     /**
35662     * fetch a  masonry brick based on the masonry brick ID
35663     * @param {string} the masonry brick to add
35664     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35665     */
35666     
35667     get: function(brick_id) 
35668     {
35669         // if (typeof(this.groups[brick_id]) == 'undefined') {
35670         //     return false;
35671         // }
35672         // return this.groups[brick_id] ;
35673         
35674         if(this.groups.key(brick_id)) {
35675             return this.groups.key(brick_id);
35676         }
35677         
35678         return false;
35679     }
35680     
35681     
35682     
35683 });
35684
35685  /*
35686  * - LGPL
35687  *
35688  * element
35689  * 
35690  */
35691
35692 /**
35693  * @class Roo.bootstrap.Brick
35694  * @extends Roo.bootstrap.Component
35695  * Bootstrap Brick class
35696  * 
35697  * @constructor
35698  * Create a new Brick
35699  * @param {Object} config The config object
35700  */
35701
35702 Roo.bootstrap.Brick = function(config){
35703     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35704     
35705     this.addEvents({
35706         // raw events
35707         /**
35708          * @event click
35709          * When a Brick is click
35710          * @param {Roo.bootstrap.Brick} this
35711          * @param {Roo.EventObject} e
35712          */
35713         "click" : true
35714     });
35715 };
35716
35717 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35718     
35719     /**
35720      * @cfg {String} title
35721      */   
35722     title : '',
35723     /**
35724      * @cfg {String} html
35725      */   
35726     html : '',
35727     /**
35728      * @cfg {String} bgimage
35729      */   
35730     bgimage : '',
35731     /**
35732      * @cfg {String} cls
35733      */   
35734     cls : '',
35735     /**
35736      * @cfg {String} href
35737      */   
35738     href : '',
35739     /**
35740      * @cfg {String} video
35741      */   
35742     video : '',
35743     /**
35744      * @cfg {Boolean} square
35745      */   
35746     square : true,
35747     
35748     getAutoCreate : function()
35749     {
35750         var cls = 'roo-brick';
35751         
35752         if(this.href.length){
35753             cls += ' roo-brick-link';
35754         }
35755         
35756         if(this.bgimage.length){
35757             cls += ' roo-brick-image';
35758         }
35759         
35760         if(!this.html.length && !this.bgimage.length){
35761             cls += ' roo-brick-center-title';
35762         }
35763         
35764         if(!this.html.length && this.bgimage.length){
35765             cls += ' roo-brick-bottom-title';
35766         }
35767         
35768         if(this.cls){
35769             cls += ' ' + this.cls;
35770         }
35771         
35772         var cfg = {
35773             tag: (this.href.length) ? 'a' : 'div',
35774             cls: cls,
35775             cn: [
35776                 {
35777                     tag: 'div',
35778                     cls: 'roo-brick-paragraph',
35779                     cn: []
35780                 }
35781             ]
35782         };
35783         
35784         if(this.href.length){
35785             cfg.href = this.href;
35786         }
35787         
35788         var cn = cfg.cn[0].cn;
35789         
35790         if(this.title.length){
35791             cn.push({
35792                 tag: 'h4',
35793                 cls: 'roo-brick-title',
35794                 html: this.title
35795             });
35796         }
35797         
35798         if(this.html.length){
35799             cn.push({
35800                 tag: 'p',
35801                 cls: 'roo-brick-text',
35802                 html: this.html
35803             });
35804         } else {
35805             cn.cls += ' hide';
35806         }
35807         
35808         if(this.bgimage.length){
35809             cfg.cn.push({
35810                 tag: 'img',
35811                 cls: 'roo-brick-image-view',
35812                 src: this.bgimage
35813             });
35814         }
35815         
35816         return cfg;
35817     },
35818     
35819     initEvents: function() 
35820     {
35821         if(this.title.length || this.html.length){
35822             this.el.on('mouseenter'  ,this.enter, this);
35823             this.el.on('mouseleave', this.leave, this);
35824         }
35825         
35826         Roo.EventManager.onWindowResize(this.resize, this); 
35827         
35828         if(this.bgimage.length){
35829             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35830             this.imageEl.on('load', this.onImageLoad, this);
35831             return;
35832         }
35833         
35834         this.resize();
35835     },
35836     
35837     onImageLoad : function()
35838     {
35839         this.resize();
35840     },
35841     
35842     resize : function()
35843     {
35844         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35845         
35846         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35847         
35848         if(this.bgimage.length){
35849             var image = this.el.select('.roo-brick-image-view', true).first();
35850             
35851             image.setWidth(paragraph.getWidth());
35852             
35853             if(this.square){
35854                 image.setHeight(paragraph.getWidth());
35855             }
35856             
35857             this.el.setHeight(image.getHeight());
35858             paragraph.setHeight(image.getHeight());
35859             
35860         }
35861         
35862     },
35863     
35864     enter: function(e, el)
35865     {
35866         e.preventDefault();
35867         
35868         if(this.bgimage.length){
35869             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35870             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35871         }
35872     },
35873     
35874     leave: function(e, el)
35875     {
35876         e.preventDefault();
35877         
35878         if(this.bgimage.length){
35879             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35880             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35881         }
35882     }
35883     
35884 });
35885
35886  
35887
35888  /*
35889  * - LGPL
35890  *
35891  * Number field 
35892  */
35893
35894 /**
35895  * @class Roo.bootstrap.NumberField
35896  * @extends Roo.bootstrap.Input
35897  * Bootstrap NumberField class
35898  * 
35899  * 
35900  * 
35901  * 
35902  * @constructor
35903  * Create a new NumberField
35904  * @param {Object} config The config object
35905  */
35906
35907 Roo.bootstrap.NumberField = function(config){
35908     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35909 };
35910
35911 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35912     
35913     /**
35914      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35915      */
35916     allowDecimals : true,
35917     /**
35918      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35919      */
35920     decimalSeparator : ".",
35921     /**
35922      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35923      */
35924     decimalPrecision : 2,
35925     /**
35926      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35927      */
35928     allowNegative : true,
35929     
35930     /**
35931      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35932      */
35933     allowZero: true,
35934     /**
35935      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35936      */
35937     minValue : Number.NEGATIVE_INFINITY,
35938     /**
35939      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35940      */
35941     maxValue : Number.MAX_VALUE,
35942     /**
35943      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35944      */
35945     minText : "The minimum value for this field is {0}",
35946     /**
35947      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35948      */
35949     maxText : "The maximum value for this field is {0}",
35950     /**
35951      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35952      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35953      */
35954     nanText : "{0} is not a valid number",
35955     /**
35956      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35957      */
35958     thousandsDelimiter : false,
35959     /**
35960      * @cfg {String} valueAlign alignment of value
35961      */
35962     valueAlign : "left",
35963
35964     getAutoCreate : function()
35965     {
35966         var hiddenInput = {
35967             tag: 'input',
35968             type: 'hidden',
35969             id: Roo.id(),
35970             cls: 'hidden-number-input'
35971         };
35972         
35973         if (this.name) {
35974             hiddenInput.name = this.name;
35975         }
35976         
35977         this.name = '';
35978         
35979         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35980         
35981         this.name = hiddenInput.name;
35982         
35983         if(cfg.cn.length > 0) {
35984             cfg.cn.push(hiddenInput);
35985         }
35986         
35987         return cfg;
35988     },
35989
35990     // private
35991     initEvents : function()
35992     {   
35993         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35994         
35995         var allowed = "0123456789";
35996         
35997         if(this.allowDecimals){
35998             allowed += this.decimalSeparator;
35999         }
36000         
36001         if(this.allowNegative){
36002             allowed += "-";
36003         }
36004         
36005         if(this.thousandsDelimiter) {
36006             allowed += ",";
36007         }
36008         
36009         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36010         
36011         var keyPress = function(e){
36012             
36013             var k = e.getKey();
36014             
36015             var c = e.getCharCode();
36016             
36017             if(
36018                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36019                     allowed.indexOf(String.fromCharCode(c)) === -1
36020             ){
36021                 e.stopEvent();
36022                 return;
36023             }
36024             
36025             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36026                 return;
36027             }
36028             
36029             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36030                 e.stopEvent();
36031             }
36032         };
36033         
36034         this.el.on("keypress", keyPress, this);
36035     },
36036     
36037     validateValue : function(value)
36038     {
36039         
36040         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36041             return false;
36042         }
36043         
36044         var num = this.parseValue(value);
36045         
36046         if(isNaN(num)){
36047             this.markInvalid(String.format(this.nanText, value));
36048             return false;
36049         }
36050         
36051         if(num < this.minValue){
36052             this.markInvalid(String.format(this.minText, this.minValue));
36053             return false;
36054         }
36055         
36056         if(num > this.maxValue){
36057             this.markInvalid(String.format(this.maxText, this.maxValue));
36058             return false;
36059         }
36060         
36061         return true;
36062     },
36063
36064     getValue : function()
36065     {
36066         var v = this.hiddenEl().getValue();
36067         
36068         return this.fixPrecision(this.parseValue(v));
36069     },
36070
36071     parseValue : function(value)
36072     {
36073         if(this.thousandsDelimiter) {
36074             value += "";
36075             r = new RegExp(",", "g");
36076             value = value.replace(r, "");
36077         }
36078         
36079         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36080         return isNaN(value) ? '' : value;
36081     },
36082
36083     fixPrecision : function(value)
36084     {
36085         if(this.thousandsDelimiter) {
36086             value += "";
36087             r = new RegExp(",", "g");
36088             value = value.replace(r, "");
36089         }
36090         
36091         var nan = isNaN(value);
36092         
36093         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36094             return nan ? '' : value;
36095         }
36096         return parseFloat(value).toFixed(this.decimalPrecision);
36097     },
36098
36099     setValue : function(v)
36100     {
36101         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36102         
36103         this.value = v;
36104         
36105         if(this.rendered){
36106             
36107             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36108             
36109             this.inputEl().dom.value = (v == '') ? '' :
36110                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36111             
36112             if(!this.allowZero && v === '0') {
36113                 this.hiddenEl().dom.value = '';
36114                 this.inputEl().dom.value = '';
36115             }
36116             
36117             this.validate();
36118         }
36119     },
36120
36121     decimalPrecisionFcn : function(v)
36122     {
36123         return Math.floor(v);
36124     },
36125
36126     beforeBlur : function()
36127     {
36128         var v = this.parseValue(this.getRawValue());
36129         
36130         if(v || v === 0 || v === ''){
36131             this.setValue(v);
36132         }
36133     },
36134     
36135     hiddenEl : function()
36136     {
36137         return this.el.select('input.hidden-number-input',true).first();
36138     }
36139     
36140 });
36141
36142  
36143
36144 /*
36145 * Licence: LGPL
36146 */
36147
36148 /**
36149  * @class Roo.bootstrap.DocumentSlider
36150  * @extends Roo.bootstrap.Component
36151  * Bootstrap DocumentSlider class
36152  * 
36153  * @constructor
36154  * Create a new DocumentViewer
36155  * @param {Object} config The config object
36156  */
36157
36158 Roo.bootstrap.DocumentSlider = function(config){
36159     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36160     
36161     this.files = [];
36162     
36163     this.addEvents({
36164         /**
36165          * @event initial
36166          * Fire after initEvent
36167          * @param {Roo.bootstrap.DocumentSlider} this
36168          */
36169         "initial" : true,
36170         /**
36171          * @event update
36172          * Fire after update
36173          * @param {Roo.bootstrap.DocumentSlider} this
36174          */
36175         "update" : true,
36176         /**
36177          * @event click
36178          * Fire after click
36179          * @param {Roo.bootstrap.DocumentSlider} this
36180          */
36181         "click" : true
36182     });
36183 };
36184
36185 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36186     
36187     files : false,
36188     
36189     indicator : 0,
36190     
36191     getAutoCreate : function()
36192     {
36193         var cfg = {
36194             tag : 'div',
36195             cls : 'roo-document-slider',
36196             cn : [
36197                 {
36198                     tag : 'div',
36199                     cls : 'roo-document-slider-header',
36200                     cn : [
36201                         {
36202                             tag : 'div',
36203                             cls : 'roo-document-slider-header-title'
36204                         }
36205                     ]
36206                 },
36207                 {
36208                     tag : 'div',
36209                     cls : 'roo-document-slider-body',
36210                     cn : [
36211                         {
36212                             tag : 'div',
36213                             cls : 'roo-document-slider-prev',
36214                             cn : [
36215                                 {
36216                                     tag : 'i',
36217                                     cls : 'fa fa-chevron-left'
36218                                 }
36219                             ]
36220                         },
36221                         {
36222                             tag : 'div',
36223                             cls : 'roo-document-slider-thumb',
36224                             cn : [
36225                                 {
36226                                     tag : 'img',
36227                                     cls : 'roo-document-slider-image'
36228                                 }
36229                             ]
36230                         },
36231                         {
36232                             tag : 'div',
36233                             cls : 'roo-document-slider-next',
36234                             cn : [
36235                                 {
36236                                     tag : 'i',
36237                                     cls : 'fa fa-chevron-right'
36238                                 }
36239                             ]
36240                         }
36241                     ]
36242                 }
36243             ]
36244         };
36245         
36246         return cfg;
36247     },
36248     
36249     initEvents : function()
36250     {
36251         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36252         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36253         
36254         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36255         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36256         
36257         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36258         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36259         
36260         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36261         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36262         
36263         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36264         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36265         
36266         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36267         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36268         
36269         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36270         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36271         
36272         this.thumbEl.on('click', this.onClick, this);
36273         
36274         this.prevIndicator.on('click', this.prev, this);
36275         
36276         this.nextIndicator.on('click', this.next, this);
36277         
36278     },
36279     
36280     initial : function()
36281     {
36282         if(this.files.length){
36283             this.indicator = 1;
36284             this.update()
36285         }
36286         
36287         this.fireEvent('initial', this);
36288     },
36289     
36290     update : function()
36291     {
36292         this.imageEl.attr('src', this.files[this.indicator - 1]);
36293         
36294         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36295         
36296         this.prevIndicator.show();
36297         
36298         if(this.indicator == 1){
36299             this.prevIndicator.hide();
36300         }
36301         
36302         this.nextIndicator.show();
36303         
36304         if(this.indicator == this.files.length){
36305             this.nextIndicator.hide();
36306         }
36307         
36308         this.thumbEl.scrollTo('top');
36309         
36310         this.fireEvent('update', this);
36311     },
36312     
36313     onClick : function(e)
36314     {
36315         e.preventDefault();
36316         
36317         this.fireEvent('click', this);
36318     },
36319     
36320     prev : function(e)
36321     {
36322         e.preventDefault();
36323         
36324         this.indicator = Math.max(1, this.indicator - 1);
36325         
36326         this.update();
36327     },
36328     
36329     next : function(e)
36330     {
36331         e.preventDefault();
36332         
36333         this.indicator = Math.min(this.files.length, this.indicator + 1);
36334         
36335         this.update();
36336     }
36337 });
36338 /*
36339  * - LGPL
36340  *
36341  * RadioSet
36342  *
36343  *
36344  */
36345
36346 /**
36347  * @class Roo.bootstrap.RadioSet
36348  * @extends Roo.bootstrap.Input
36349  * Bootstrap RadioSet class
36350  * @cfg {String} indicatorpos (left|right) default left
36351  * @cfg {Boolean} inline (true|false) inline the element (default true)
36352  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36353  * @constructor
36354  * Create a new RadioSet
36355  * @param {Object} config The config object
36356  */
36357
36358 Roo.bootstrap.RadioSet = function(config){
36359     
36360     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36361     
36362     this.radioes = [];
36363     
36364     Roo.bootstrap.RadioSet.register(this);
36365     
36366     this.addEvents({
36367         /**
36368         * @event check
36369         * Fires when the element is checked or unchecked.
36370         * @param {Roo.bootstrap.RadioSet} this This radio
36371         * @param {Roo.bootstrap.Radio} item The checked item
36372         */
36373        check : true,
36374        /**
36375         * @event click
36376         * Fires when the element is click.
36377         * @param {Roo.bootstrap.RadioSet} this This radio set
36378         * @param {Roo.bootstrap.Radio} item The checked item
36379         * @param {Roo.EventObject} e The event object
36380         */
36381        click : true
36382     });
36383     
36384 };
36385
36386 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36387
36388     radioes : false,
36389     
36390     inline : true,
36391     
36392     weight : '',
36393     
36394     indicatorpos : 'left',
36395     
36396     getAutoCreate : function()
36397     {
36398         var label = {
36399             tag : 'label',
36400             cls : 'roo-radio-set-label',
36401             cn : [
36402                 {
36403                     tag : 'span',
36404                     html : this.fieldLabel
36405                 }
36406             ]
36407         };
36408         if (Roo.bootstrap.version == 3) {
36409             
36410             
36411             if(this.indicatorpos == 'left'){
36412                 label.cn.unshift({
36413                     tag : 'i',
36414                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36415                     tooltip : 'This field is required'
36416                 });
36417             } else {
36418                 label.cn.push({
36419                     tag : 'i',
36420                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36421                     tooltip : 'This field is required'
36422                 });
36423             }
36424         }
36425         var items = {
36426             tag : 'div',
36427             cls : 'roo-radio-set-items'
36428         };
36429         
36430         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36431         
36432         if (align === 'left' && this.fieldLabel.length) {
36433             
36434             items = {
36435                 cls : "roo-radio-set-right", 
36436                 cn: [
36437                     items
36438                 ]
36439             };
36440             
36441             if(this.labelWidth > 12){
36442                 label.style = "width: " + this.labelWidth + 'px';
36443             }
36444             
36445             if(this.labelWidth < 13 && this.labelmd == 0){
36446                 this.labelmd = this.labelWidth;
36447             }
36448             
36449             if(this.labellg > 0){
36450                 label.cls += ' col-lg-' + this.labellg;
36451                 items.cls += ' col-lg-' + (12 - this.labellg);
36452             }
36453             
36454             if(this.labelmd > 0){
36455                 label.cls += ' col-md-' + this.labelmd;
36456                 items.cls += ' col-md-' + (12 - this.labelmd);
36457             }
36458             
36459             if(this.labelsm > 0){
36460                 label.cls += ' col-sm-' + this.labelsm;
36461                 items.cls += ' col-sm-' + (12 - this.labelsm);
36462             }
36463             
36464             if(this.labelxs > 0){
36465                 label.cls += ' col-xs-' + this.labelxs;
36466                 items.cls += ' col-xs-' + (12 - this.labelxs);
36467             }
36468         }
36469         
36470         var cfg = {
36471             tag : 'div',
36472             cls : 'roo-radio-set',
36473             cn : [
36474                 {
36475                     tag : 'input',
36476                     cls : 'roo-radio-set-input',
36477                     type : 'hidden',
36478                     name : this.name,
36479                     value : this.value ? this.value :  ''
36480                 },
36481                 label,
36482                 items
36483             ]
36484         };
36485         
36486         if(this.weight.length){
36487             cfg.cls += ' roo-radio-' + this.weight;
36488         }
36489         
36490         if(this.inline) {
36491             cfg.cls += ' roo-radio-set-inline';
36492         }
36493         
36494         var settings=this;
36495         ['xs','sm','md','lg'].map(function(size){
36496             if (settings[size]) {
36497                 cfg.cls += ' col-' + size + '-' + settings[size];
36498             }
36499         });
36500         
36501         return cfg;
36502         
36503     },
36504
36505     initEvents : function()
36506     {
36507         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36508         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36509         
36510         if(!this.fieldLabel.length){
36511             this.labelEl.hide();
36512         }
36513         
36514         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36515         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36516         
36517         this.indicator = this.indicatorEl();
36518         
36519         if(this.indicator){
36520             this.indicator.addClass('invisible');
36521         }
36522         
36523         this.originalValue = this.getValue();
36524         
36525     },
36526     
36527     inputEl: function ()
36528     {
36529         return this.el.select('.roo-radio-set-input', true).first();
36530     },
36531     
36532     getChildContainer : function()
36533     {
36534         return this.itemsEl;
36535     },
36536     
36537     register : function(item)
36538     {
36539         this.radioes.push(item);
36540         
36541     },
36542     
36543     validate : function()
36544     {   
36545         if(this.getVisibilityEl().hasClass('hidden')){
36546             return true;
36547         }
36548         
36549         var valid = false;
36550         
36551         Roo.each(this.radioes, function(i){
36552             if(!i.checked){
36553                 return;
36554             }
36555             
36556             valid = true;
36557             return false;
36558         });
36559         
36560         if(this.allowBlank) {
36561             return true;
36562         }
36563         
36564         if(this.disabled || valid){
36565             this.markValid();
36566             return true;
36567         }
36568         
36569         this.markInvalid();
36570         return false;
36571         
36572     },
36573     
36574     markValid : function()
36575     {
36576         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36577             this.indicatorEl().removeClass('visible');
36578             this.indicatorEl().addClass('invisible');
36579         }
36580         
36581         
36582         if (Roo.bootstrap.version == 3) {
36583             this.el.removeClass([this.invalidClass, this.validClass]);
36584             this.el.addClass(this.validClass);
36585         } else {
36586             this.el.removeClass(['is-invalid','is-valid']);
36587             this.el.addClass(['is-valid']);
36588         }
36589         this.fireEvent('valid', this);
36590     },
36591     
36592     markInvalid : function(msg)
36593     {
36594         if(this.allowBlank || this.disabled){
36595             return;
36596         }
36597         
36598         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36599             this.indicatorEl().removeClass('invisible');
36600             this.indicatorEl().addClass('visible');
36601         }
36602         if (Roo.bootstrap.version == 3) {
36603             this.el.removeClass([this.invalidClass, this.validClass]);
36604             this.el.addClass(this.invalidClass);
36605         } else {
36606             this.el.removeClass(['is-invalid','is-valid']);
36607             this.el.addClass(['is-invalid']);
36608         }
36609         
36610         this.fireEvent('invalid', this, msg);
36611         
36612     },
36613     
36614     setValue : function(v, suppressEvent)
36615     {   
36616         if(this.value === v){
36617             return;
36618         }
36619         
36620         this.value = v;
36621         
36622         if(this.rendered){
36623             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36624         }
36625         
36626         Roo.each(this.radioes, function(i){
36627             i.checked = false;
36628             i.el.removeClass('checked');
36629         });
36630         
36631         Roo.each(this.radioes, function(i){
36632             
36633             if(i.value === v || i.value.toString() === v.toString()){
36634                 i.checked = true;
36635                 i.el.addClass('checked');
36636                 
36637                 if(suppressEvent !== true){
36638                     this.fireEvent('check', this, i);
36639                 }
36640                 
36641                 return false;
36642             }
36643             
36644         }, this);
36645         
36646         this.validate();
36647     },
36648     
36649     clearInvalid : function(){
36650         
36651         if(!this.el || this.preventMark){
36652             return;
36653         }
36654         
36655         this.el.removeClass([this.invalidClass]);
36656         
36657         this.fireEvent('valid', this);
36658     }
36659     
36660 });
36661
36662 Roo.apply(Roo.bootstrap.RadioSet, {
36663     
36664     groups: {},
36665     
36666     register : function(set)
36667     {
36668         this.groups[set.name] = set;
36669     },
36670     
36671     get: function(name) 
36672     {
36673         if (typeof(this.groups[name]) == 'undefined') {
36674             return false;
36675         }
36676         
36677         return this.groups[name] ;
36678     }
36679     
36680 });
36681 /*
36682  * Based on:
36683  * Ext JS Library 1.1.1
36684  * Copyright(c) 2006-2007, Ext JS, LLC.
36685  *
36686  * Originally Released Under LGPL - original licence link has changed is not relivant.
36687  *
36688  * Fork - LGPL
36689  * <script type="text/javascript">
36690  */
36691
36692
36693 /**
36694  * @class Roo.bootstrap.SplitBar
36695  * @extends Roo.util.Observable
36696  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36697  * <br><br>
36698  * Usage:
36699  * <pre><code>
36700 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36701                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36702 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36703 split.minSize = 100;
36704 split.maxSize = 600;
36705 split.animate = true;
36706 split.on('moved', splitterMoved);
36707 </code></pre>
36708  * @constructor
36709  * Create a new SplitBar
36710  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36711  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36712  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36713  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36714                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36715                         position of the SplitBar).
36716  */
36717 Roo.bootstrap.SplitBar = function(cfg){
36718     
36719     /** @private */
36720     
36721     //{
36722     //  dragElement : elm
36723     //  resizingElement: el,
36724         // optional..
36725     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36726     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36727         // existingProxy ???
36728     //}
36729     
36730     this.el = Roo.get(cfg.dragElement, true);
36731     this.el.dom.unselectable = "on";
36732     /** @private */
36733     this.resizingEl = Roo.get(cfg.resizingElement, true);
36734
36735     /**
36736      * @private
36737      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36738      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36739      * @type Number
36740      */
36741     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36742     
36743     /**
36744      * The minimum size of the resizing element. (Defaults to 0)
36745      * @type Number
36746      */
36747     this.minSize = 0;
36748     
36749     /**
36750      * The maximum size of the resizing element. (Defaults to 2000)
36751      * @type Number
36752      */
36753     this.maxSize = 2000;
36754     
36755     /**
36756      * Whether to animate the transition to the new size
36757      * @type Boolean
36758      */
36759     this.animate = false;
36760     
36761     /**
36762      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36763      * @type Boolean
36764      */
36765     this.useShim = false;
36766     
36767     /** @private */
36768     this.shim = null;
36769     
36770     if(!cfg.existingProxy){
36771         /** @private */
36772         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36773     }else{
36774         this.proxy = Roo.get(cfg.existingProxy).dom;
36775     }
36776     /** @private */
36777     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36778     
36779     /** @private */
36780     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36781     
36782     /** @private */
36783     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36784     
36785     /** @private */
36786     this.dragSpecs = {};
36787     
36788     /**
36789      * @private The adapter to use to positon and resize elements
36790      */
36791     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36792     this.adapter.init(this);
36793     
36794     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36795         /** @private */
36796         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36797         this.el.addClass("roo-splitbar-h");
36798     }else{
36799         /** @private */
36800         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36801         this.el.addClass("roo-splitbar-v");
36802     }
36803     
36804     this.addEvents({
36805         /**
36806          * @event resize
36807          * Fires when the splitter is moved (alias for {@link #event-moved})
36808          * @param {Roo.bootstrap.SplitBar} this
36809          * @param {Number} newSize the new width or height
36810          */
36811         "resize" : true,
36812         /**
36813          * @event moved
36814          * Fires when the splitter is moved
36815          * @param {Roo.bootstrap.SplitBar} this
36816          * @param {Number} newSize the new width or height
36817          */
36818         "moved" : true,
36819         /**
36820          * @event beforeresize
36821          * Fires before the splitter is dragged
36822          * @param {Roo.bootstrap.SplitBar} this
36823          */
36824         "beforeresize" : true,
36825
36826         "beforeapply" : true
36827     });
36828
36829     Roo.util.Observable.call(this);
36830 };
36831
36832 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36833     onStartProxyDrag : function(x, y){
36834         this.fireEvent("beforeresize", this);
36835         if(!this.overlay){
36836             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36837             o.unselectable();
36838             o.enableDisplayMode("block");
36839             // all splitbars share the same overlay
36840             Roo.bootstrap.SplitBar.prototype.overlay = o;
36841         }
36842         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36843         this.overlay.show();
36844         Roo.get(this.proxy).setDisplayed("block");
36845         var size = this.adapter.getElementSize(this);
36846         this.activeMinSize = this.getMinimumSize();;
36847         this.activeMaxSize = this.getMaximumSize();;
36848         var c1 = size - this.activeMinSize;
36849         var c2 = Math.max(this.activeMaxSize - size, 0);
36850         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36851             this.dd.resetConstraints();
36852             this.dd.setXConstraint(
36853                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36854                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36855             );
36856             this.dd.setYConstraint(0, 0);
36857         }else{
36858             this.dd.resetConstraints();
36859             this.dd.setXConstraint(0, 0);
36860             this.dd.setYConstraint(
36861                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36862                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36863             );
36864          }
36865         this.dragSpecs.startSize = size;
36866         this.dragSpecs.startPoint = [x, y];
36867         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36868     },
36869     
36870     /** 
36871      * @private Called after the drag operation by the DDProxy
36872      */
36873     onEndProxyDrag : function(e){
36874         Roo.get(this.proxy).setDisplayed(false);
36875         var endPoint = Roo.lib.Event.getXY(e);
36876         if(this.overlay){
36877             this.overlay.hide();
36878         }
36879         var newSize;
36880         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36881             newSize = this.dragSpecs.startSize + 
36882                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36883                     endPoint[0] - this.dragSpecs.startPoint[0] :
36884                     this.dragSpecs.startPoint[0] - endPoint[0]
36885                 );
36886         }else{
36887             newSize = this.dragSpecs.startSize + 
36888                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36889                     endPoint[1] - this.dragSpecs.startPoint[1] :
36890                     this.dragSpecs.startPoint[1] - endPoint[1]
36891                 );
36892         }
36893         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36894         if(newSize != this.dragSpecs.startSize){
36895             if(this.fireEvent('beforeapply', this, newSize) !== false){
36896                 this.adapter.setElementSize(this, newSize);
36897                 this.fireEvent("moved", this, newSize);
36898                 this.fireEvent("resize", this, newSize);
36899             }
36900         }
36901     },
36902     
36903     /**
36904      * Get the adapter this SplitBar uses
36905      * @return The adapter object
36906      */
36907     getAdapter : function(){
36908         return this.adapter;
36909     },
36910     
36911     /**
36912      * Set the adapter this SplitBar uses
36913      * @param {Object} adapter A SplitBar adapter object
36914      */
36915     setAdapter : function(adapter){
36916         this.adapter = adapter;
36917         this.adapter.init(this);
36918     },
36919     
36920     /**
36921      * Gets the minimum size for the resizing element
36922      * @return {Number} The minimum size
36923      */
36924     getMinimumSize : function(){
36925         return this.minSize;
36926     },
36927     
36928     /**
36929      * Sets the minimum size for the resizing element
36930      * @param {Number} minSize The minimum size
36931      */
36932     setMinimumSize : function(minSize){
36933         this.minSize = minSize;
36934     },
36935     
36936     /**
36937      * Gets the maximum size for the resizing element
36938      * @return {Number} The maximum size
36939      */
36940     getMaximumSize : function(){
36941         return this.maxSize;
36942     },
36943     
36944     /**
36945      * Sets the maximum size for the resizing element
36946      * @param {Number} maxSize The maximum size
36947      */
36948     setMaximumSize : function(maxSize){
36949         this.maxSize = maxSize;
36950     },
36951     
36952     /**
36953      * Sets the initialize size for the resizing element
36954      * @param {Number} size The initial size
36955      */
36956     setCurrentSize : function(size){
36957         var oldAnimate = this.animate;
36958         this.animate = false;
36959         this.adapter.setElementSize(this, size);
36960         this.animate = oldAnimate;
36961     },
36962     
36963     /**
36964      * Destroy this splitbar. 
36965      * @param {Boolean} removeEl True to remove the element
36966      */
36967     destroy : function(removeEl){
36968         if(this.shim){
36969             this.shim.remove();
36970         }
36971         this.dd.unreg();
36972         this.proxy.parentNode.removeChild(this.proxy);
36973         if(removeEl){
36974             this.el.remove();
36975         }
36976     }
36977 });
36978
36979 /**
36980  * @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.
36981  */
36982 Roo.bootstrap.SplitBar.createProxy = function(dir){
36983     var proxy = new Roo.Element(document.createElement("div"));
36984     proxy.unselectable();
36985     var cls = 'roo-splitbar-proxy';
36986     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36987     document.body.appendChild(proxy.dom);
36988     return proxy.dom;
36989 };
36990
36991 /** 
36992  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36993  * Default Adapter. It assumes the splitter and resizing element are not positioned
36994  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36995  */
36996 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36997 };
36998
36999 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37000     // do nothing for now
37001     init : function(s){
37002     
37003     },
37004     /**
37005      * Called before drag operations to get the current size of the resizing element. 
37006      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37007      */
37008      getElementSize : function(s){
37009         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37010             return s.resizingEl.getWidth();
37011         }else{
37012             return s.resizingEl.getHeight();
37013         }
37014     },
37015     
37016     /**
37017      * Called after drag operations to set the size of the resizing element.
37018      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37019      * @param {Number} newSize The new size to set
37020      * @param {Function} onComplete A function to be invoked when resizing is complete
37021      */
37022     setElementSize : function(s, newSize, onComplete){
37023         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37024             if(!s.animate){
37025                 s.resizingEl.setWidth(newSize);
37026                 if(onComplete){
37027                     onComplete(s, newSize);
37028                 }
37029             }else{
37030                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37031             }
37032         }else{
37033             
37034             if(!s.animate){
37035                 s.resizingEl.setHeight(newSize);
37036                 if(onComplete){
37037                     onComplete(s, newSize);
37038                 }
37039             }else{
37040                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37041             }
37042         }
37043     }
37044 };
37045
37046 /** 
37047  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37048  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37049  * Adapter that  moves the splitter element to align with the resized sizing element. 
37050  * Used with an absolute positioned SplitBar.
37051  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37052  * document.body, make sure you assign an id to the body element.
37053  */
37054 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37055     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37056     this.container = Roo.get(container);
37057 };
37058
37059 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37060     init : function(s){
37061         this.basic.init(s);
37062     },
37063     
37064     getElementSize : function(s){
37065         return this.basic.getElementSize(s);
37066     },
37067     
37068     setElementSize : function(s, newSize, onComplete){
37069         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37070     },
37071     
37072     moveSplitter : function(s){
37073         var yes = Roo.bootstrap.SplitBar;
37074         switch(s.placement){
37075             case yes.LEFT:
37076                 s.el.setX(s.resizingEl.getRight());
37077                 break;
37078             case yes.RIGHT:
37079                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37080                 break;
37081             case yes.TOP:
37082                 s.el.setY(s.resizingEl.getBottom());
37083                 break;
37084             case yes.BOTTOM:
37085                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37086                 break;
37087         }
37088     }
37089 };
37090
37091 /**
37092  * Orientation constant - Create a vertical SplitBar
37093  * @static
37094  * @type Number
37095  */
37096 Roo.bootstrap.SplitBar.VERTICAL = 1;
37097
37098 /**
37099  * Orientation constant - Create a horizontal SplitBar
37100  * @static
37101  * @type Number
37102  */
37103 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37104
37105 /**
37106  * Placement constant - The resizing element is to the left of the splitter element
37107  * @static
37108  * @type Number
37109  */
37110 Roo.bootstrap.SplitBar.LEFT = 1;
37111
37112 /**
37113  * Placement constant - The resizing element is to the right of the splitter element
37114  * @static
37115  * @type Number
37116  */
37117 Roo.bootstrap.SplitBar.RIGHT = 2;
37118
37119 /**
37120  * Placement constant - The resizing element is positioned above the splitter element
37121  * @static
37122  * @type Number
37123  */
37124 Roo.bootstrap.SplitBar.TOP = 3;
37125
37126 /**
37127  * Placement constant - The resizing element is positioned under splitter element
37128  * @static
37129  * @type Number
37130  */
37131 Roo.bootstrap.SplitBar.BOTTOM = 4;
37132 Roo.namespace("Roo.bootstrap.layout");/*
37133  * Based on:
37134  * Ext JS Library 1.1.1
37135  * Copyright(c) 2006-2007, Ext JS, LLC.
37136  *
37137  * Originally Released Under LGPL - original licence link has changed is not relivant.
37138  *
37139  * Fork - LGPL
37140  * <script type="text/javascript">
37141  */
37142
37143 /**
37144  * @class Roo.bootstrap.layout.Manager
37145  * @extends Roo.bootstrap.Component
37146  * Base class for layout managers.
37147  */
37148 Roo.bootstrap.layout.Manager = function(config)
37149 {
37150     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37151
37152
37153
37154
37155
37156     /** false to disable window resize monitoring @type Boolean */
37157     this.monitorWindowResize = true;
37158     this.regions = {};
37159     this.addEvents({
37160         /**
37161          * @event layout
37162          * Fires when a layout is performed.
37163          * @param {Roo.LayoutManager} this
37164          */
37165         "layout" : true,
37166         /**
37167          * @event regionresized
37168          * Fires when the user resizes a region.
37169          * @param {Roo.LayoutRegion} region The resized region
37170          * @param {Number} newSize The new size (width for east/west, height for north/south)
37171          */
37172         "regionresized" : true,
37173         /**
37174          * @event regioncollapsed
37175          * Fires when a region is collapsed.
37176          * @param {Roo.LayoutRegion} region The collapsed region
37177          */
37178         "regioncollapsed" : true,
37179         /**
37180          * @event regionexpanded
37181          * Fires when a region is expanded.
37182          * @param {Roo.LayoutRegion} region The expanded region
37183          */
37184         "regionexpanded" : true
37185     });
37186     this.updating = false;
37187
37188     if (config.el) {
37189         this.el = Roo.get(config.el);
37190         this.initEvents();
37191     }
37192
37193 };
37194
37195 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37196
37197
37198     regions : null,
37199
37200     monitorWindowResize : true,
37201
37202
37203     updating : false,
37204
37205
37206     onRender : function(ct, position)
37207     {
37208         if(!this.el){
37209             this.el = Roo.get(ct);
37210             this.initEvents();
37211         }
37212         //this.fireEvent('render',this);
37213     },
37214
37215
37216     initEvents: function()
37217     {
37218
37219
37220         // ie scrollbar fix
37221         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37222             document.body.scroll = "no";
37223         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37224             this.el.position('relative');
37225         }
37226         this.id = this.el.id;
37227         this.el.addClass("roo-layout-container");
37228         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37229         if(this.el.dom != document.body ) {
37230             this.el.on('resize', this.layout,this);
37231             this.el.on('show', this.layout,this);
37232         }
37233
37234     },
37235
37236     /**
37237      * Returns true if this layout is currently being updated
37238      * @return {Boolean}
37239      */
37240     isUpdating : function(){
37241         return this.updating;
37242     },
37243
37244     /**
37245      * Suspend the LayoutManager from doing auto-layouts while
37246      * making multiple add or remove calls
37247      */
37248     beginUpdate : function(){
37249         this.updating = true;
37250     },
37251
37252     /**
37253      * Restore auto-layouts and optionally disable the manager from performing a layout
37254      * @param {Boolean} noLayout true to disable a layout update
37255      */
37256     endUpdate : function(noLayout){
37257         this.updating = false;
37258         if(!noLayout){
37259             this.layout();
37260         }
37261     },
37262
37263     layout: function(){
37264         // abstract...
37265     },
37266
37267     onRegionResized : function(region, newSize){
37268         this.fireEvent("regionresized", region, newSize);
37269         this.layout();
37270     },
37271
37272     onRegionCollapsed : function(region){
37273         this.fireEvent("regioncollapsed", region);
37274     },
37275
37276     onRegionExpanded : function(region){
37277         this.fireEvent("regionexpanded", region);
37278     },
37279
37280     /**
37281      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37282      * performs box-model adjustments.
37283      * @return {Object} The size as an object {width: (the width), height: (the height)}
37284      */
37285     getViewSize : function()
37286     {
37287         var size;
37288         if(this.el.dom != document.body){
37289             size = this.el.getSize();
37290         }else{
37291             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37292         }
37293         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37294         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37295         return size;
37296     },
37297
37298     /**
37299      * Returns the Element this layout is bound to.
37300      * @return {Roo.Element}
37301      */
37302     getEl : function(){
37303         return this.el;
37304     },
37305
37306     /**
37307      * Returns the specified region.
37308      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37309      * @return {Roo.LayoutRegion}
37310      */
37311     getRegion : function(target){
37312         return this.regions[target.toLowerCase()];
37313     },
37314
37315     onWindowResize : function(){
37316         if(this.monitorWindowResize){
37317             this.layout();
37318         }
37319     }
37320 });
37321 /*
37322  * Based on:
37323  * Ext JS Library 1.1.1
37324  * Copyright(c) 2006-2007, Ext JS, LLC.
37325  *
37326  * Originally Released Under LGPL - original licence link has changed is not relivant.
37327  *
37328  * Fork - LGPL
37329  * <script type="text/javascript">
37330  */
37331 /**
37332  * @class Roo.bootstrap.layout.Border
37333  * @extends Roo.bootstrap.layout.Manager
37334  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37335  * please see: examples/bootstrap/nested.html<br><br>
37336  
37337 <b>The container the layout is rendered into can be either the body element or any other element.
37338 If it is not the body element, the container needs to either be an absolute positioned element,
37339 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37340 the container size if it is not the body element.</b>
37341
37342 * @constructor
37343 * Create a new Border
37344 * @param {Object} config Configuration options
37345  */
37346 Roo.bootstrap.layout.Border = function(config){
37347     config = config || {};
37348     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37349     
37350     
37351     
37352     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37353         if(config[region]){
37354             config[region].region = region;
37355             this.addRegion(config[region]);
37356         }
37357     },this);
37358     
37359 };
37360
37361 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37362
37363 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37364     
37365     parent : false, // this might point to a 'nest' or a ???
37366     
37367     /**
37368      * Creates and adds a new region if it doesn't already exist.
37369      * @param {String} target The target region key (north, south, east, west or center).
37370      * @param {Object} config The regions config object
37371      * @return {BorderLayoutRegion} The new region
37372      */
37373     addRegion : function(config)
37374     {
37375         if(!this.regions[config.region]){
37376             var r = this.factory(config);
37377             this.bindRegion(r);
37378         }
37379         return this.regions[config.region];
37380     },
37381
37382     // private (kinda)
37383     bindRegion : function(r){
37384         this.regions[r.config.region] = r;
37385         
37386         r.on("visibilitychange",    this.layout, this);
37387         r.on("paneladded",          this.layout, this);
37388         r.on("panelremoved",        this.layout, this);
37389         r.on("invalidated",         this.layout, this);
37390         r.on("resized",             this.onRegionResized, this);
37391         r.on("collapsed",           this.onRegionCollapsed, this);
37392         r.on("expanded",            this.onRegionExpanded, this);
37393     },
37394
37395     /**
37396      * Performs a layout update.
37397      */
37398     layout : function()
37399     {
37400         if(this.updating) {
37401             return;
37402         }
37403         
37404         // render all the rebions if they have not been done alreayd?
37405         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37406             if(this.regions[region] && !this.regions[region].bodyEl){
37407                 this.regions[region].onRender(this.el)
37408             }
37409         },this);
37410         
37411         var size = this.getViewSize();
37412         var w = size.width;
37413         var h = size.height;
37414         var centerW = w;
37415         var centerH = h;
37416         var centerY = 0;
37417         var centerX = 0;
37418         //var x = 0, y = 0;
37419
37420         var rs = this.regions;
37421         var north = rs["north"];
37422         var south = rs["south"]; 
37423         var west = rs["west"];
37424         var east = rs["east"];
37425         var center = rs["center"];
37426         //if(this.hideOnLayout){ // not supported anymore
37427             //c.el.setStyle("display", "none");
37428         //}
37429         if(north && north.isVisible()){
37430             var b = north.getBox();
37431             var m = north.getMargins();
37432             b.width = w - (m.left+m.right);
37433             b.x = m.left;
37434             b.y = m.top;
37435             centerY = b.height + b.y + m.bottom;
37436             centerH -= centerY;
37437             north.updateBox(this.safeBox(b));
37438         }
37439         if(south && south.isVisible()){
37440             var b = south.getBox();
37441             var m = south.getMargins();
37442             b.width = w - (m.left+m.right);
37443             b.x = m.left;
37444             var totalHeight = (b.height + m.top + m.bottom);
37445             b.y = h - totalHeight + m.top;
37446             centerH -= totalHeight;
37447             south.updateBox(this.safeBox(b));
37448         }
37449         if(west && west.isVisible()){
37450             var b = west.getBox();
37451             var m = west.getMargins();
37452             b.height = centerH - (m.top+m.bottom);
37453             b.x = m.left;
37454             b.y = centerY + m.top;
37455             var totalWidth = (b.width + m.left + m.right);
37456             centerX += totalWidth;
37457             centerW -= totalWidth;
37458             west.updateBox(this.safeBox(b));
37459         }
37460         if(east && east.isVisible()){
37461             var b = east.getBox();
37462             var m = east.getMargins();
37463             b.height = centerH - (m.top+m.bottom);
37464             var totalWidth = (b.width + m.left + m.right);
37465             b.x = w - totalWidth + m.left;
37466             b.y = centerY + m.top;
37467             centerW -= totalWidth;
37468             east.updateBox(this.safeBox(b));
37469         }
37470         if(center){
37471             var m = center.getMargins();
37472             var centerBox = {
37473                 x: centerX + m.left,
37474                 y: centerY + m.top,
37475                 width: centerW - (m.left+m.right),
37476                 height: centerH - (m.top+m.bottom)
37477             };
37478             //if(this.hideOnLayout){
37479                 //center.el.setStyle("display", "block");
37480             //}
37481             center.updateBox(this.safeBox(centerBox));
37482         }
37483         this.el.repaint();
37484         this.fireEvent("layout", this);
37485     },
37486
37487     // private
37488     safeBox : function(box){
37489         box.width = Math.max(0, box.width);
37490         box.height = Math.max(0, box.height);
37491         return box;
37492     },
37493
37494     /**
37495      * Adds a ContentPanel (or subclass) to this layout.
37496      * @param {String} target The target region key (north, south, east, west or center).
37497      * @param {Roo.ContentPanel} panel The panel to add
37498      * @return {Roo.ContentPanel} The added panel
37499      */
37500     add : function(target, panel){
37501          
37502         target = target.toLowerCase();
37503         return this.regions[target].add(panel);
37504     },
37505
37506     /**
37507      * Remove a ContentPanel (or subclass) to this layout.
37508      * @param {String} target The target region key (north, south, east, west or center).
37509      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37510      * @return {Roo.ContentPanel} The removed panel
37511      */
37512     remove : function(target, panel){
37513         target = target.toLowerCase();
37514         return this.regions[target].remove(panel);
37515     },
37516
37517     /**
37518      * Searches all regions for a panel with the specified id
37519      * @param {String} panelId
37520      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37521      */
37522     findPanel : function(panelId){
37523         var rs = this.regions;
37524         for(var target in rs){
37525             if(typeof rs[target] != "function"){
37526                 var p = rs[target].getPanel(panelId);
37527                 if(p){
37528                     return p;
37529                 }
37530             }
37531         }
37532         return null;
37533     },
37534
37535     /**
37536      * Searches all regions for a panel with the specified id and activates (shows) it.
37537      * @param {String/ContentPanel} panelId The panels id or the panel itself
37538      * @return {Roo.ContentPanel} The shown panel or null
37539      */
37540     showPanel : function(panelId) {
37541       var rs = this.regions;
37542       for(var target in rs){
37543          var r = rs[target];
37544          if(typeof r != "function"){
37545             if(r.hasPanel(panelId)){
37546                return r.showPanel(panelId);
37547             }
37548          }
37549       }
37550       return null;
37551    },
37552
37553    /**
37554      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37555      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37556      */
37557    /*
37558     restoreState : function(provider){
37559         if(!provider){
37560             provider = Roo.state.Manager;
37561         }
37562         var sm = new Roo.LayoutStateManager();
37563         sm.init(this, provider);
37564     },
37565 */
37566  
37567  
37568     /**
37569      * Adds a xtype elements to the layout.
37570      * <pre><code>
37571
37572 layout.addxtype({
37573        xtype : 'ContentPanel',
37574        region: 'west',
37575        items: [ .... ]
37576    }
37577 );
37578
37579 layout.addxtype({
37580         xtype : 'NestedLayoutPanel',
37581         region: 'west',
37582         layout: {
37583            center: { },
37584            west: { }   
37585         },
37586         items : [ ... list of content panels or nested layout panels.. ]
37587    }
37588 );
37589 </code></pre>
37590      * @param {Object} cfg Xtype definition of item to add.
37591      */
37592     addxtype : function(cfg)
37593     {
37594         // basically accepts a pannel...
37595         // can accept a layout region..!?!?
37596         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37597         
37598         
37599         // theory?  children can only be panels??
37600         
37601         //if (!cfg.xtype.match(/Panel$/)) {
37602         //    return false;
37603         //}
37604         var ret = false;
37605         
37606         if (typeof(cfg.region) == 'undefined') {
37607             Roo.log("Failed to add Panel, region was not set");
37608             Roo.log(cfg);
37609             return false;
37610         }
37611         var region = cfg.region;
37612         delete cfg.region;
37613         
37614           
37615         var xitems = [];
37616         if (cfg.items) {
37617             xitems = cfg.items;
37618             delete cfg.items;
37619         }
37620         var nb = false;
37621         
37622         if ( region == 'center') {
37623             Roo.log("Center: " + cfg.title);
37624         }
37625         
37626         
37627         switch(cfg.xtype) 
37628         {
37629             case 'Content':  // ContentPanel (el, cfg)
37630             case 'Scroll':  // ContentPanel (el, cfg)
37631             case 'View': 
37632                 cfg.autoCreate = cfg.autoCreate || true;
37633                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37634                 //} else {
37635                 //    var el = this.el.createChild();
37636                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37637                 //}
37638                 
37639                 this.add(region, ret);
37640                 break;
37641             
37642             /*
37643             case 'TreePanel': // our new panel!
37644                 cfg.el = this.el.createChild();
37645                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37646                 this.add(region, ret);
37647                 break;
37648             */
37649             
37650             case 'Nest': 
37651                 // create a new Layout (which is  a Border Layout...
37652                 
37653                 var clayout = cfg.layout;
37654                 clayout.el  = this.el.createChild();
37655                 clayout.items   = clayout.items  || [];
37656                 
37657                 delete cfg.layout;
37658                 
37659                 // replace this exitems with the clayout ones..
37660                 xitems = clayout.items;
37661                  
37662                 // force background off if it's in center...
37663                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37664                     cfg.background = false;
37665                 }
37666                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37667                 
37668                 
37669                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37670                 //console.log('adding nested layout panel '  + cfg.toSource());
37671                 this.add(region, ret);
37672                 nb = {}; /// find first...
37673                 break;
37674             
37675             case 'Grid':
37676                 
37677                 // needs grid and region
37678                 
37679                 //var el = this.getRegion(region).el.createChild();
37680                 /*
37681                  *var el = this.el.createChild();
37682                 // create the grid first...
37683                 cfg.grid.container = el;
37684                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37685                 */
37686                 
37687                 if (region == 'center' && this.active ) {
37688                     cfg.background = false;
37689                 }
37690                 
37691                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37692                 
37693                 this.add(region, ret);
37694                 /*
37695                 if (cfg.background) {
37696                     // render grid on panel activation (if panel background)
37697                     ret.on('activate', function(gp) {
37698                         if (!gp.grid.rendered) {
37699                     //        gp.grid.render(el);
37700                         }
37701                     });
37702                 } else {
37703                   //  cfg.grid.render(el);
37704                 }
37705                 */
37706                 break;
37707            
37708            
37709             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37710                 // it was the old xcomponent building that caused this before.
37711                 // espeically if border is the top element in the tree.
37712                 ret = this;
37713                 break; 
37714                 
37715                     
37716                 
37717                 
37718                 
37719             default:
37720                 /*
37721                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37722                     
37723                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37724                     this.add(region, ret);
37725                 } else {
37726                 */
37727                     Roo.log(cfg);
37728                     throw "Can not add '" + cfg.xtype + "' to Border";
37729                     return null;
37730              
37731                                 
37732              
37733         }
37734         this.beginUpdate();
37735         // add children..
37736         var region = '';
37737         var abn = {};
37738         Roo.each(xitems, function(i)  {
37739             region = nb && i.region ? i.region : false;
37740             
37741             var add = ret.addxtype(i);
37742            
37743             if (region) {
37744                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37745                 if (!i.background) {
37746                     abn[region] = nb[region] ;
37747                 }
37748             }
37749             
37750         });
37751         this.endUpdate();
37752
37753         // make the last non-background panel active..
37754         //if (nb) { Roo.log(abn); }
37755         if (nb) {
37756             
37757             for(var r in abn) {
37758                 region = this.getRegion(r);
37759                 if (region) {
37760                     // tried using nb[r], but it does not work..
37761                      
37762                     region.showPanel(abn[r]);
37763                    
37764                 }
37765             }
37766         }
37767         return ret;
37768         
37769     },
37770     
37771     
37772 // private
37773     factory : function(cfg)
37774     {
37775         
37776         var validRegions = Roo.bootstrap.layout.Border.regions;
37777
37778         var target = cfg.region;
37779         cfg.mgr = this;
37780         
37781         var r = Roo.bootstrap.layout;
37782         Roo.log(target);
37783         switch(target){
37784             case "north":
37785                 return new r.North(cfg);
37786             case "south":
37787                 return new r.South(cfg);
37788             case "east":
37789                 return new r.East(cfg);
37790             case "west":
37791                 return new r.West(cfg);
37792             case "center":
37793                 return new r.Center(cfg);
37794         }
37795         throw 'Layout region "'+target+'" not supported.';
37796     }
37797     
37798     
37799 });
37800  /*
37801  * Based on:
37802  * Ext JS Library 1.1.1
37803  * Copyright(c) 2006-2007, Ext JS, LLC.
37804  *
37805  * Originally Released Under LGPL - original licence link has changed is not relivant.
37806  *
37807  * Fork - LGPL
37808  * <script type="text/javascript">
37809  */
37810  
37811 /**
37812  * @class Roo.bootstrap.layout.Basic
37813  * @extends Roo.util.Observable
37814  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37815  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37816  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37817  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37818  * @cfg {string}   region  the region that it inhabits..
37819  * @cfg {bool}   skipConfig skip config?
37820  * 
37821
37822  */
37823 Roo.bootstrap.layout.Basic = function(config){
37824     
37825     this.mgr = config.mgr;
37826     
37827     this.position = config.region;
37828     
37829     var skipConfig = config.skipConfig;
37830     
37831     this.events = {
37832         /**
37833          * @scope Roo.BasicLayoutRegion
37834          */
37835         
37836         /**
37837          * @event beforeremove
37838          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37839          * @param {Roo.LayoutRegion} this
37840          * @param {Roo.ContentPanel} panel The panel
37841          * @param {Object} e The cancel event object
37842          */
37843         "beforeremove" : true,
37844         /**
37845          * @event invalidated
37846          * Fires when the layout for this region is changed.
37847          * @param {Roo.LayoutRegion} this
37848          */
37849         "invalidated" : true,
37850         /**
37851          * @event visibilitychange
37852          * Fires when this region is shown or hidden 
37853          * @param {Roo.LayoutRegion} this
37854          * @param {Boolean} visibility true or false
37855          */
37856         "visibilitychange" : true,
37857         /**
37858          * @event paneladded
37859          * Fires when a panel is added. 
37860          * @param {Roo.LayoutRegion} this
37861          * @param {Roo.ContentPanel} panel The panel
37862          */
37863         "paneladded" : true,
37864         /**
37865          * @event panelremoved
37866          * Fires when a panel is removed. 
37867          * @param {Roo.LayoutRegion} this
37868          * @param {Roo.ContentPanel} panel The panel
37869          */
37870         "panelremoved" : true,
37871         /**
37872          * @event beforecollapse
37873          * Fires when this region before collapse.
37874          * @param {Roo.LayoutRegion} this
37875          */
37876         "beforecollapse" : true,
37877         /**
37878          * @event collapsed
37879          * Fires when this region is collapsed.
37880          * @param {Roo.LayoutRegion} this
37881          */
37882         "collapsed" : true,
37883         /**
37884          * @event expanded
37885          * Fires when this region is expanded.
37886          * @param {Roo.LayoutRegion} this
37887          */
37888         "expanded" : true,
37889         /**
37890          * @event slideshow
37891          * Fires when this region is slid into view.
37892          * @param {Roo.LayoutRegion} this
37893          */
37894         "slideshow" : true,
37895         /**
37896          * @event slidehide
37897          * Fires when this region slides out of view. 
37898          * @param {Roo.LayoutRegion} this
37899          */
37900         "slidehide" : true,
37901         /**
37902          * @event panelactivated
37903          * Fires when a panel is activated. 
37904          * @param {Roo.LayoutRegion} this
37905          * @param {Roo.ContentPanel} panel The activated panel
37906          */
37907         "panelactivated" : true,
37908         /**
37909          * @event resized
37910          * Fires when the user resizes this region. 
37911          * @param {Roo.LayoutRegion} this
37912          * @param {Number} newSize The new size (width for east/west, height for north/south)
37913          */
37914         "resized" : true
37915     };
37916     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37917     this.panels = new Roo.util.MixedCollection();
37918     this.panels.getKey = this.getPanelId.createDelegate(this);
37919     this.box = null;
37920     this.activePanel = null;
37921     // ensure listeners are added...
37922     
37923     if (config.listeners || config.events) {
37924         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37925             listeners : config.listeners || {},
37926             events : config.events || {}
37927         });
37928     }
37929     
37930     if(skipConfig !== true){
37931         this.applyConfig(config);
37932     }
37933 };
37934
37935 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37936 {
37937     getPanelId : function(p){
37938         return p.getId();
37939     },
37940     
37941     applyConfig : function(config){
37942         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37943         this.config = config;
37944         
37945     },
37946     
37947     /**
37948      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37949      * the width, for horizontal (north, south) the height.
37950      * @param {Number} newSize The new width or height
37951      */
37952     resizeTo : function(newSize){
37953         var el = this.el ? this.el :
37954                  (this.activePanel ? this.activePanel.getEl() : null);
37955         if(el){
37956             switch(this.position){
37957                 case "east":
37958                 case "west":
37959                     el.setWidth(newSize);
37960                     this.fireEvent("resized", this, newSize);
37961                 break;
37962                 case "north":
37963                 case "south":
37964                     el.setHeight(newSize);
37965                     this.fireEvent("resized", this, newSize);
37966                 break;                
37967             }
37968         }
37969     },
37970     
37971     getBox : function(){
37972         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37973     },
37974     
37975     getMargins : function(){
37976         return this.margins;
37977     },
37978     
37979     updateBox : function(box){
37980         this.box = box;
37981         var el = this.activePanel.getEl();
37982         el.dom.style.left = box.x + "px";
37983         el.dom.style.top = box.y + "px";
37984         this.activePanel.setSize(box.width, box.height);
37985     },
37986     
37987     /**
37988      * Returns the container element for this region.
37989      * @return {Roo.Element}
37990      */
37991     getEl : function(){
37992         return this.activePanel;
37993     },
37994     
37995     /**
37996      * Returns true if this region is currently visible.
37997      * @return {Boolean}
37998      */
37999     isVisible : function(){
38000         return this.activePanel ? true : false;
38001     },
38002     
38003     setActivePanel : function(panel){
38004         panel = this.getPanel(panel);
38005         if(this.activePanel && this.activePanel != panel){
38006             this.activePanel.setActiveState(false);
38007             this.activePanel.getEl().setLeftTop(-10000,-10000);
38008         }
38009         this.activePanel = panel;
38010         panel.setActiveState(true);
38011         if(this.box){
38012             panel.setSize(this.box.width, this.box.height);
38013         }
38014         this.fireEvent("panelactivated", this, panel);
38015         this.fireEvent("invalidated");
38016     },
38017     
38018     /**
38019      * Show the specified panel.
38020      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38021      * @return {Roo.ContentPanel} The shown panel or null
38022      */
38023     showPanel : function(panel){
38024         panel = this.getPanel(panel);
38025         if(panel){
38026             this.setActivePanel(panel);
38027         }
38028         return panel;
38029     },
38030     
38031     /**
38032      * Get the active panel for this region.
38033      * @return {Roo.ContentPanel} The active panel or null
38034      */
38035     getActivePanel : function(){
38036         return this.activePanel;
38037     },
38038     
38039     /**
38040      * Add the passed ContentPanel(s)
38041      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38042      * @return {Roo.ContentPanel} The panel added (if only one was added)
38043      */
38044     add : function(panel){
38045         if(arguments.length > 1){
38046             for(var i = 0, len = arguments.length; i < len; i++) {
38047                 this.add(arguments[i]);
38048             }
38049             return null;
38050         }
38051         if(this.hasPanel(panel)){
38052             this.showPanel(panel);
38053             return panel;
38054         }
38055         var el = panel.getEl();
38056         if(el.dom.parentNode != this.mgr.el.dom){
38057             this.mgr.el.dom.appendChild(el.dom);
38058         }
38059         if(panel.setRegion){
38060             panel.setRegion(this);
38061         }
38062         this.panels.add(panel);
38063         el.setStyle("position", "absolute");
38064         if(!panel.background){
38065             this.setActivePanel(panel);
38066             if(this.config.initialSize && this.panels.getCount()==1){
38067                 this.resizeTo(this.config.initialSize);
38068             }
38069         }
38070         this.fireEvent("paneladded", this, panel);
38071         return panel;
38072     },
38073     
38074     /**
38075      * Returns true if the panel is in this region.
38076      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38077      * @return {Boolean}
38078      */
38079     hasPanel : function(panel){
38080         if(typeof panel == "object"){ // must be panel obj
38081             panel = panel.getId();
38082         }
38083         return this.getPanel(panel) ? true : false;
38084     },
38085     
38086     /**
38087      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38088      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38089      * @param {Boolean} preservePanel Overrides the config preservePanel option
38090      * @return {Roo.ContentPanel} The panel that was removed
38091      */
38092     remove : function(panel, preservePanel){
38093         panel = this.getPanel(panel);
38094         if(!panel){
38095             return null;
38096         }
38097         var e = {};
38098         this.fireEvent("beforeremove", this, panel, e);
38099         if(e.cancel === true){
38100             return null;
38101         }
38102         var panelId = panel.getId();
38103         this.panels.removeKey(panelId);
38104         return panel;
38105     },
38106     
38107     /**
38108      * Returns the panel specified or null if it's not in this region.
38109      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38110      * @return {Roo.ContentPanel}
38111      */
38112     getPanel : function(id){
38113         if(typeof id == "object"){ // must be panel obj
38114             return id;
38115         }
38116         return this.panels.get(id);
38117     },
38118     
38119     /**
38120      * Returns this regions position (north/south/east/west/center).
38121      * @return {String} 
38122      */
38123     getPosition: function(){
38124         return this.position;    
38125     }
38126 });/*
38127  * Based on:
38128  * Ext JS Library 1.1.1
38129  * Copyright(c) 2006-2007, Ext JS, LLC.
38130  *
38131  * Originally Released Under LGPL - original licence link has changed is not relivant.
38132  *
38133  * Fork - LGPL
38134  * <script type="text/javascript">
38135  */
38136  
38137 /**
38138  * @class Roo.bootstrap.layout.Region
38139  * @extends Roo.bootstrap.layout.Basic
38140  * This class represents a region in a layout manager.
38141  
38142  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38143  * @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})
38144  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38145  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38146  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38147  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38148  * @cfg {String}    title           The title for the region (overrides panel titles)
38149  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38150  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38151  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38152  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38153  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38154  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38155  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38156  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38157  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38158  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38159
38160  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38161  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38162  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38163  * @cfg {Number}    width           For East/West panels
38164  * @cfg {Number}    height          For North/South panels
38165  * @cfg {Boolean}   split           To show the splitter
38166  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38167  * 
38168  * @cfg {string}   cls             Extra CSS classes to add to region
38169  * 
38170  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38171  * @cfg {string}   region  the region that it inhabits..
38172  *
38173
38174  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38175  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38176
38177  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38178  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38179  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38180  */
38181 Roo.bootstrap.layout.Region = function(config)
38182 {
38183     this.applyConfig(config);
38184
38185     var mgr = config.mgr;
38186     var pos = config.region;
38187     config.skipConfig = true;
38188     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38189     
38190     if (mgr.el) {
38191         this.onRender(mgr.el);   
38192     }
38193      
38194     this.visible = true;
38195     this.collapsed = false;
38196     this.unrendered_panels = [];
38197 };
38198
38199 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38200
38201     position: '', // set by wrapper (eg. north/south etc..)
38202     unrendered_panels : null,  // unrendered panels.
38203     
38204     tabPosition : false,
38205     
38206     mgr: false, // points to 'Border'
38207     
38208     
38209     createBody : function(){
38210         /** This region's body element 
38211         * @type Roo.Element */
38212         this.bodyEl = this.el.createChild({
38213                 tag: "div",
38214                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38215         });
38216     },
38217
38218     onRender: function(ctr, pos)
38219     {
38220         var dh = Roo.DomHelper;
38221         /** This region's container element 
38222         * @type Roo.Element */
38223         this.el = dh.append(ctr.dom, {
38224                 tag: "div",
38225                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38226             }, true);
38227         /** This region's title element 
38228         * @type Roo.Element */
38229     
38230         this.titleEl = dh.append(this.el.dom,  {
38231                 tag: "div",
38232                 unselectable: "on",
38233                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38234                 children:[
38235                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38236                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38237                 ]
38238             }, true);
38239         
38240         this.titleEl.enableDisplayMode();
38241         /** This region's title text element 
38242         * @type HTMLElement */
38243         this.titleTextEl = this.titleEl.dom.firstChild;
38244         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38245         /*
38246         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38247         this.closeBtn.enableDisplayMode();
38248         this.closeBtn.on("click", this.closeClicked, this);
38249         this.closeBtn.hide();
38250     */
38251         this.createBody(this.config);
38252         if(this.config.hideWhenEmpty){
38253             this.hide();
38254             this.on("paneladded", this.validateVisibility, this);
38255             this.on("panelremoved", this.validateVisibility, this);
38256         }
38257         if(this.autoScroll){
38258             this.bodyEl.setStyle("overflow", "auto");
38259         }else{
38260             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38261         }
38262         //if(c.titlebar !== false){
38263             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38264                 this.titleEl.hide();
38265             }else{
38266                 this.titleEl.show();
38267                 if(this.config.title){
38268                     this.titleTextEl.innerHTML = this.config.title;
38269                 }
38270             }
38271         //}
38272         if(this.config.collapsed){
38273             this.collapse(true);
38274         }
38275         if(this.config.hidden){
38276             this.hide();
38277         }
38278         
38279         if (this.unrendered_panels && this.unrendered_panels.length) {
38280             for (var i =0;i< this.unrendered_panels.length; i++) {
38281                 this.add(this.unrendered_panels[i]);
38282             }
38283             this.unrendered_panels = null;
38284             
38285         }
38286         
38287     },
38288     
38289     applyConfig : function(c)
38290     {
38291         /*
38292          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38293             var dh = Roo.DomHelper;
38294             if(c.titlebar !== false){
38295                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38296                 this.collapseBtn.on("click", this.collapse, this);
38297                 this.collapseBtn.enableDisplayMode();
38298                 /*
38299                 if(c.showPin === true || this.showPin){
38300                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38301                     this.stickBtn.enableDisplayMode();
38302                     this.stickBtn.on("click", this.expand, this);
38303                     this.stickBtn.hide();
38304                 }
38305                 
38306             }
38307             */
38308             /** This region's collapsed element
38309             * @type Roo.Element */
38310             /*
38311              *
38312             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38313                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38314             ]}, true);
38315             
38316             if(c.floatable !== false){
38317                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38318                this.collapsedEl.on("click", this.collapseClick, this);
38319             }
38320
38321             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38322                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38323                    id: "message", unselectable: "on", style:{"float":"left"}});
38324                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38325              }
38326             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38327             this.expandBtn.on("click", this.expand, this);
38328             
38329         }
38330         
38331         if(this.collapseBtn){
38332             this.collapseBtn.setVisible(c.collapsible == true);
38333         }
38334         
38335         this.cmargins = c.cmargins || this.cmargins ||
38336                          (this.position == "west" || this.position == "east" ?
38337                              {top: 0, left: 2, right:2, bottom: 0} :
38338                              {top: 2, left: 0, right:0, bottom: 2});
38339         */
38340         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38341         
38342         
38343         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38344         
38345         this.autoScroll = c.autoScroll || false;
38346         
38347         
38348        
38349         
38350         this.duration = c.duration || .30;
38351         this.slideDuration = c.slideDuration || .45;
38352         this.config = c;
38353        
38354     },
38355     /**
38356      * Returns true if this region is currently visible.
38357      * @return {Boolean}
38358      */
38359     isVisible : function(){
38360         return this.visible;
38361     },
38362
38363     /**
38364      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38365      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38366      */
38367     //setCollapsedTitle : function(title){
38368     //    title = title || "&#160;";
38369      //   if(this.collapsedTitleTextEl){
38370       //      this.collapsedTitleTextEl.innerHTML = title;
38371        // }
38372     //},
38373
38374     getBox : function(){
38375         var b;
38376       //  if(!this.collapsed){
38377             b = this.el.getBox(false, true);
38378        // }else{
38379           //  b = this.collapsedEl.getBox(false, true);
38380         //}
38381         return b;
38382     },
38383
38384     getMargins : function(){
38385         return this.margins;
38386         //return this.collapsed ? this.cmargins : this.margins;
38387     },
38388 /*
38389     highlight : function(){
38390         this.el.addClass("x-layout-panel-dragover");
38391     },
38392
38393     unhighlight : function(){
38394         this.el.removeClass("x-layout-panel-dragover");
38395     },
38396 */
38397     updateBox : function(box)
38398     {
38399         if (!this.bodyEl) {
38400             return; // not rendered yet..
38401         }
38402         
38403         this.box = box;
38404         if(!this.collapsed){
38405             this.el.dom.style.left = box.x + "px";
38406             this.el.dom.style.top = box.y + "px";
38407             this.updateBody(box.width, box.height);
38408         }else{
38409             this.collapsedEl.dom.style.left = box.x + "px";
38410             this.collapsedEl.dom.style.top = box.y + "px";
38411             this.collapsedEl.setSize(box.width, box.height);
38412         }
38413         if(this.tabs){
38414             this.tabs.autoSizeTabs();
38415         }
38416     },
38417
38418     updateBody : function(w, h)
38419     {
38420         if(w !== null){
38421             this.el.setWidth(w);
38422             w -= this.el.getBorderWidth("rl");
38423             if(this.config.adjustments){
38424                 w += this.config.adjustments[0];
38425             }
38426         }
38427         if(h !== null && h > 0){
38428             this.el.setHeight(h);
38429             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38430             h -= this.el.getBorderWidth("tb");
38431             if(this.config.adjustments){
38432                 h += this.config.adjustments[1];
38433             }
38434             this.bodyEl.setHeight(h);
38435             if(this.tabs){
38436                 h = this.tabs.syncHeight(h);
38437             }
38438         }
38439         if(this.panelSize){
38440             w = w !== null ? w : this.panelSize.width;
38441             h = h !== null ? h : this.panelSize.height;
38442         }
38443         if(this.activePanel){
38444             var el = this.activePanel.getEl();
38445             w = w !== null ? w : el.getWidth();
38446             h = h !== null ? h : el.getHeight();
38447             this.panelSize = {width: w, height: h};
38448             this.activePanel.setSize(w, h);
38449         }
38450         if(Roo.isIE && this.tabs){
38451             this.tabs.el.repaint();
38452         }
38453     },
38454
38455     /**
38456      * Returns the container element for this region.
38457      * @return {Roo.Element}
38458      */
38459     getEl : function(){
38460         return this.el;
38461     },
38462
38463     /**
38464      * Hides this region.
38465      */
38466     hide : function(){
38467         //if(!this.collapsed){
38468             this.el.dom.style.left = "-2000px";
38469             this.el.hide();
38470         //}else{
38471          //   this.collapsedEl.dom.style.left = "-2000px";
38472          //   this.collapsedEl.hide();
38473        // }
38474         this.visible = false;
38475         this.fireEvent("visibilitychange", this, false);
38476     },
38477
38478     /**
38479      * Shows this region if it was previously hidden.
38480      */
38481     show : function(){
38482         //if(!this.collapsed){
38483             this.el.show();
38484         //}else{
38485         //    this.collapsedEl.show();
38486        // }
38487         this.visible = true;
38488         this.fireEvent("visibilitychange", this, true);
38489     },
38490 /*
38491     closeClicked : function(){
38492         if(this.activePanel){
38493             this.remove(this.activePanel);
38494         }
38495     },
38496
38497     collapseClick : function(e){
38498         if(this.isSlid){
38499            e.stopPropagation();
38500            this.slideIn();
38501         }else{
38502            e.stopPropagation();
38503            this.slideOut();
38504         }
38505     },
38506 */
38507     /**
38508      * Collapses this region.
38509      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38510      */
38511     /*
38512     collapse : function(skipAnim, skipCheck = false){
38513         if(this.collapsed) {
38514             return;
38515         }
38516         
38517         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38518             
38519             this.collapsed = true;
38520             if(this.split){
38521                 this.split.el.hide();
38522             }
38523             if(this.config.animate && skipAnim !== true){
38524                 this.fireEvent("invalidated", this);
38525                 this.animateCollapse();
38526             }else{
38527                 this.el.setLocation(-20000,-20000);
38528                 this.el.hide();
38529                 this.collapsedEl.show();
38530                 this.fireEvent("collapsed", this);
38531                 this.fireEvent("invalidated", this);
38532             }
38533         }
38534         
38535     },
38536 */
38537     animateCollapse : function(){
38538         // overridden
38539     },
38540
38541     /**
38542      * Expands this region if it was previously collapsed.
38543      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38544      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38545      */
38546     /*
38547     expand : function(e, skipAnim){
38548         if(e) {
38549             e.stopPropagation();
38550         }
38551         if(!this.collapsed || this.el.hasActiveFx()) {
38552             return;
38553         }
38554         if(this.isSlid){
38555             this.afterSlideIn();
38556             skipAnim = true;
38557         }
38558         this.collapsed = false;
38559         if(this.config.animate && skipAnim !== true){
38560             this.animateExpand();
38561         }else{
38562             this.el.show();
38563             if(this.split){
38564                 this.split.el.show();
38565             }
38566             this.collapsedEl.setLocation(-2000,-2000);
38567             this.collapsedEl.hide();
38568             this.fireEvent("invalidated", this);
38569             this.fireEvent("expanded", this);
38570         }
38571     },
38572 */
38573     animateExpand : function(){
38574         // overridden
38575     },
38576
38577     initTabs : function()
38578     {
38579         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38580         
38581         var ts = new Roo.bootstrap.panel.Tabs({
38582             el: this.bodyEl.dom,
38583             region : this,
38584             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38585             disableTooltips: this.config.disableTabTips,
38586             toolbar : this.config.toolbar
38587         });
38588         
38589         if(this.config.hideTabs){
38590             ts.stripWrap.setDisplayed(false);
38591         }
38592         this.tabs = ts;
38593         ts.resizeTabs = this.config.resizeTabs === true;
38594         ts.minTabWidth = this.config.minTabWidth || 40;
38595         ts.maxTabWidth = this.config.maxTabWidth || 250;
38596         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38597         ts.monitorResize = false;
38598         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38599         ts.bodyEl.addClass('roo-layout-tabs-body');
38600         this.panels.each(this.initPanelAsTab, this);
38601     },
38602
38603     initPanelAsTab : function(panel){
38604         var ti = this.tabs.addTab(
38605             panel.getEl().id,
38606             panel.getTitle(),
38607             null,
38608             this.config.closeOnTab && panel.isClosable(),
38609             panel.tpl
38610         );
38611         if(panel.tabTip !== undefined){
38612             ti.setTooltip(panel.tabTip);
38613         }
38614         ti.on("activate", function(){
38615               this.setActivePanel(panel);
38616         }, this);
38617         
38618         if(this.config.closeOnTab){
38619             ti.on("beforeclose", function(t, e){
38620                 e.cancel = true;
38621                 this.remove(panel);
38622             }, this);
38623         }
38624         
38625         panel.tabItem = ti;
38626         
38627         return ti;
38628     },
38629
38630     updatePanelTitle : function(panel, title)
38631     {
38632         if(this.activePanel == panel){
38633             this.updateTitle(title);
38634         }
38635         if(this.tabs){
38636             var ti = this.tabs.getTab(panel.getEl().id);
38637             ti.setText(title);
38638             if(panel.tabTip !== undefined){
38639                 ti.setTooltip(panel.tabTip);
38640             }
38641         }
38642     },
38643
38644     updateTitle : function(title){
38645         if(this.titleTextEl && !this.config.title){
38646             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38647         }
38648     },
38649
38650     setActivePanel : function(panel)
38651     {
38652         panel = this.getPanel(panel);
38653         if(this.activePanel && this.activePanel != panel){
38654             if(this.activePanel.setActiveState(false) === false){
38655                 return;
38656             }
38657         }
38658         this.activePanel = panel;
38659         panel.setActiveState(true);
38660         if(this.panelSize){
38661             panel.setSize(this.panelSize.width, this.panelSize.height);
38662         }
38663         if(this.closeBtn){
38664             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38665         }
38666         this.updateTitle(panel.getTitle());
38667         if(this.tabs){
38668             this.fireEvent("invalidated", this);
38669         }
38670         this.fireEvent("panelactivated", this, panel);
38671     },
38672
38673     /**
38674      * Shows the specified panel.
38675      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38676      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38677      */
38678     showPanel : function(panel)
38679     {
38680         panel = this.getPanel(panel);
38681         if(panel){
38682             if(this.tabs){
38683                 var tab = this.tabs.getTab(panel.getEl().id);
38684                 if(tab.isHidden()){
38685                     this.tabs.unhideTab(tab.id);
38686                 }
38687                 tab.activate();
38688             }else{
38689                 this.setActivePanel(panel);
38690             }
38691         }
38692         return panel;
38693     },
38694
38695     /**
38696      * Get the active panel for this region.
38697      * @return {Roo.ContentPanel} The active panel or null
38698      */
38699     getActivePanel : function(){
38700         return this.activePanel;
38701     },
38702
38703     validateVisibility : function(){
38704         if(this.panels.getCount() < 1){
38705             this.updateTitle("&#160;");
38706             this.closeBtn.hide();
38707             this.hide();
38708         }else{
38709             if(!this.isVisible()){
38710                 this.show();
38711             }
38712         }
38713     },
38714
38715     /**
38716      * Adds the passed ContentPanel(s) to this region.
38717      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38718      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38719      */
38720     add : function(panel)
38721     {
38722         if(arguments.length > 1){
38723             for(var i = 0, len = arguments.length; i < len; i++) {
38724                 this.add(arguments[i]);
38725             }
38726             return null;
38727         }
38728         
38729         // if we have not been rendered yet, then we can not really do much of this..
38730         if (!this.bodyEl) {
38731             this.unrendered_panels.push(panel);
38732             return panel;
38733         }
38734         
38735         
38736         
38737         
38738         if(this.hasPanel(panel)){
38739             this.showPanel(panel);
38740             return panel;
38741         }
38742         panel.setRegion(this);
38743         this.panels.add(panel);
38744        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38745             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38746             // and hide them... ???
38747             this.bodyEl.dom.appendChild(panel.getEl().dom);
38748             if(panel.background !== true){
38749                 this.setActivePanel(panel);
38750             }
38751             this.fireEvent("paneladded", this, panel);
38752             return panel;
38753         }
38754         */
38755         if(!this.tabs){
38756             this.initTabs();
38757         }else{
38758             this.initPanelAsTab(panel);
38759         }
38760         
38761         
38762         if(panel.background !== true){
38763             this.tabs.activate(panel.getEl().id);
38764         }
38765         this.fireEvent("paneladded", this, panel);
38766         return panel;
38767     },
38768
38769     /**
38770      * Hides the tab for the specified panel.
38771      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38772      */
38773     hidePanel : function(panel){
38774         if(this.tabs && (panel = this.getPanel(panel))){
38775             this.tabs.hideTab(panel.getEl().id);
38776         }
38777     },
38778
38779     /**
38780      * Unhides the tab for a previously hidden panel.
38781      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38782      */
38783     unhidePanel : function(panel){
38784         if(this.tabs && (panel = this.getPanel(panel))){
38785             this.tabs.unhideTab(panel.getEl().id);
38786         }
38787     },
38788
38789     clearPanels : function(){
38790         while(this.panels.getCount() > 0){
38791              this.remove(this.panels.first());
38792         }
38793     },
38794
38795     /**
38796      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38797      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38798      * @param {Boolean} preservePanel Overrides the config preservePanel option
38799      * @return {Roo.ContentPanel} The panel that was removed
38800      */
38801     remove : function(panel, preservePanel)
38802     {
38803         panel = this.getPanel(panel);
38804         if(!panel){
38805             return null;
38806         }
38807         var e = {};
38808         this.fireEvent("beforeremove", this, panel, e);
38809         if(e.cancel === true){
38810             return null;
38811         }
38812         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38813         var panelId = panel.getId();
38814         this.panels.removeKey(panelId);
38815         if(preservePanel){
38816             document.body.appendChild(panel.getEl().dom);
38817         }
38818         if(this.tabs){
38819             this.tabs.removeTab(panel.getEl().id);
38820         }else if (!preservePanel){
38821             this.bodyEl.dom.removeChild(panel.getEl().dom);
38822         }
38823         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38824             var p = this.panels.first();
38825             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38826             tempEl.appendChild(p.getEl().dom);
38827             this.bodyEl.update("");
38828             this.bodyEl.dom.appendChild(p.getEl().dom);
38829             tempEl = null;
38830             this.updateTitle(p.getTitle());
38831             this.tabs = null;
38832             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38833             this.setActivePanel(p);
38834         }
38835         panel.setRegion(null);
38836         if(this.activePanel == panel){
38837             this.activePanel = null;
38838         }
38839         if(this.config.autoDestroy !== false && preservePanel !== true){
38840             try{panel.destroy();}catch(e){}
38841         }
38842         this.fireEvent("panelremoved", this, panel);
38843         return panel;
38844     },
38845
38846     /**
38847      * Returns the TabPanel component used by this region
38848      * @return {Roo.TabPanel}
38849      */
38850     getTabs : function(){
38851         return this.tabs;
38852     },
38853
38854     createTool : function(parentEl, className){
38855         var btn = Roo.DomHelper.append(parentEl, {
38856             tag: "div",
38857             cls: "x-layout-tools-button",
38858             children: [ {
38859                 tag: "div",
38860                 cls: "roo-layout-tools-button-inner " + className,
38861                 html: "&#160;"
38862             }]
38863         }, true);
38864         btn.addClassOnOver("roo-layout-tools-button-over");
38865         return btn;
38866     }
38867 });/*
38868  * Based on:
38869  * Ext JS Library 1.1.1
38870  * Copyright(c) 2006-2007, Ext JS, LLC.
38871  *
38872  * Originally Released Under LGPL - original licence link has changed is not relivant.
38873  *
38874  * Fork - LGPL
38875  * <script type="text/javascript">
38876  */
38877  
38878
38879
38880 /**
38881  * @class Roo.SplitLayoutRegion
38882  * @extends Roo.LayoutRegion
38883  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38884  */
38885 Roo.bootstrap.layout.Split = function(config){
38886     this.cursor = config.cursor;
38887     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38888 };
38889
38890 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38891 {
38892     splitTip : "Drag to resize.",
38893     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38894     useSplitTips : false,
38895
38896     applyConfig : function(config){
38897         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38898     },
38899     
38900     onRender : function(ctr,pos) {
38901         
38902         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38903         if(!this.config.split){
38904             return;
38905         }
38906         if(!this.split){
38907             
38908             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38909                             tag: "div",
38910                             id: this.el.id + "-split",
38911                             cls: "roo-layout-split roo-layout-split-"+this.position,
38912                             html: "&#160;"
38913             });
38914             /** The SplitBar for this region 
38915             * @type Roo.SplitBar */
38916             // does not exist yet...
38917             Roo.log([this.position, this.orientation]);
38918             
38919             this.split = new Roo.bootstrap.SplitBar({
38920                 dragElement : splitEl,
38921                 resizingElement: this.el,
38922                 orientation : this.orientation
38923             });
38924             
38925             this.split.on("moved", this.onSplitMove, this);
38926             this.split.useShim = this.config.useShim === true;
38927             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38928             if(this.useSplitTips){
38929                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38930             }
38931             //if(config.collapsible){
38932             //    this.split.el.on("dblclick", this.collapse,  this);
38933             //}
38934         }
38935         if(typeof this.config.minSize != "undefined"){
38936             this.split.minSize = this.config.minSize;
38937         }
38938         if(typeof this.config.maxSize != "undefined"){
38939             this.split.maxSize = this.config.maxSize;
38940         }
38941         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38942             this.hideSplitter();
38943         }
38944         
38945     },
38946
38947     getHMaxSize : function(){
38948          var cmax = this.config.maxSize || 10000;
38949          var center = this.mgr.getRegion("center");
38950          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38951     },
38952
38953     getVMaxSize : function(){
38954          var cmax = this.config.maxSize || 10000;
38955          var center = this.mgr.getRegion("center");
38956          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38957     },
38958
38959     onSplitMove : function(split, newSize){
38960         this.fireEvent("resized", this, newSize);
38961     },
38962     
38963     /** 
38964      * Returns the {@link Roo.SplitBar} for this region.
38965      * @return {Roo.SplitBar}
38966      */
38967     getSplitBar : function(){
38968         return this.split;
38969     },
38970     
38971     hide : function(){
38972         this.hideSplitter();
38973         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38974     },
38975
38976     hideSplitter : function(){
38977         if(this.split){
38978             this.split.el.setLocation(-2000,-2000);
38979             this.split.el.hide();
38980         }
38981     },
38982
38983     show : function(){
38984         if(this.split){
38985             this.split.el.show();
38986         }
38987         Roo.bootstrap.layout.Split.superclass.show.call(this);
38988     },
38989     
38990     beforeSlide: function(){
38991         if(Roo.isGecko){// firefox overflow auto bug workaround
38992             this.bodyEl.clip();
38993             if(this.tabs) {
38994                 this.tabs.bodyEl.clip();
38995             }
38996             if(this.activePanel){
38997                 this.activePanel.getEl().clip();
38998                 
38999                 if(this.activePanel.beforeSlide){
39000                     this.activePanel.beforeSlide();
39001                 }
39002             }
39003         }
39004     },
39005     
39006     afterSlide : function(){
39007         if(Roo.isGecko){// firefox overflow auto bug workaround
39008             this.bodyEl.unclip();
39009             if(this.tabs) {
39010                 this.tabs.bodyEl.unclip();
39011             }
39012             if(this.activePanel){
39013                 this.activePanel.getEl().unclip();
39014                 if(this.activePanel.afterSlide){
39015                     this.activePanel.afterSlide();
39016                 }
39017             }
39018         }
39019     },
39020
39021     initAutoHide : function(){
39022         if(this.autoHide !== false){
39023             if(!this.autoHideHd){
39024                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39025                 this.autoHideHd = {
39026                     "mouseout": function(e){
39027                         if(!e.within(this.el, true)){
39028                             st.delay(500);
39029                         }
39030                     },
39031                     "mouseover" : function(e){
39032                         st.cancel();
39033                     },
39034                     scope : this
39035                 };
39036             }
39037             this.el.on(this.autoHideHd);
39038         }
39039     },
39040
39041     clearAutoHide : function(){
39042         if(this.autoHide !== false){
39043             this.el.un("mouseout", this.autoHideHd.mouseout);
39044             this.el.un("mouseover", this.autoHideHd.mouseover);
39045         }
39046     },
39047
39048     clearMonitor : function(){
39049         Roo.get(document).un("click", this.slideInIf, this);
39050     },
39051
39052     // these names are backwards but not changed for compat
39053     slideOut : function(){
39054         if(this.isSlid || this.el.hasActiveFx()){
39055             return;
39056         }
39057         this.isSlid = true;
39058         if(this.collapseBtn){
39059             this.collapseBtn.hide();
39060         }
39061         this.closeBtnState = this.closeBtn.getStyle('display');
39062         this.closeBtn.hide();
39063         if(this.stickBtn){
39064             this.stickBtn.show();
39065         }
39066         this.el.show();
39067         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39068         this.beforeSlide();
39069         this.el.setStyle("z-index", 10001);
39070         this.el.slideIn(this.getSlideAnchor(), {
39071             callback: function(){
39072                 this.afterSlide();
39073                 this.initAutoHide();
39074                 Roo.get(document).on("click", this.slideInIf, this);
39075                 this.fireEvent("slideshow", this);
39076             },
39077             scope: this,
39078             block: true
39079         });
39080     },
39081
39082     afterSlideIn : function(){
39083         this.clearAutoHide();
39084         this.isSlid = false;
39085         this.clearMonitor();
39086         this.el.setStyle("z-index", "");
39087         if(this.collapseBtn){
39088             this.collapseBtn.show();
39089         }
39090         this.closeBtn.setStyle('display', this.closeBtnState);
39091         if(this.stickBtn){
39092             this.stickBtn.hide();
39093         }
39094         this.fireEvent("slidehide", this);
39095     },
39096
39097     slideIn : function(cb){
39098         if(!this.isSlid || this.el.hasActiveFx()){
39099             Roo.callback(cb);
39100             return;
39101         }
39102         this.isSlid = false;
39103         this.beforeSlide();
39104         this.el.slideOut(this.getSlideAnchor(), {
39105             callback: function(){
39106                 this.el.setLeftTop(-10000, -10000);
39107                 this.afterSlide();
39108                 this.afterSlideIn();
39109                 Roo.callback(cb);
39110             },
39111             scope: this,
39112             block: true
39113         });
39114     },
39115     
39116     slideInIf : function(e){
39117         if(!e.within(this.el)){
39118             this.slideIn();
39119         }
39120     },
39121
39122     animateCollapse : function(){
39123         this.beforeSlide();
39124         this.el.setStyle("z-index", 20000);
39125         var anchor = this.getSlideAnchor();
39126         this.el.slideOut(anchor, {
39127             callback : function(){
39128                 this.el.setStyle("z-index", "");
39129                 this.collapsedEl.slideIn(anchor, {duration:.3});
39130                 this.afterSlide();
39131                 this.el.setLocation(-10000,-10000);
39132                 this.el.hide();
39133                 this.fireEvent("collapsed", this);
39134             },
39135             scope: this,
39136             block: true
39137         });
39138     },
39139
39140     animateExpand : function(){
39141         this.beforeSlide();
39142         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39143         this.el.setStyle("z-index", 20000);
39144         this.collapsedEl.hide({
39145             duration:.1
39146         });
39147         this.el.slideIn(this.getSlideAnchor(), {
39148             callback : function(){
39149                 this.el.setStyle("z-index", "");
39150                 this.afterSlide();
39151                 if(this.split){
39152                     this.split.el.show();
39153                 }
39154                 this.fireEvent("invalidated", this);
39155                 this.fireEvent("expanded", this);
39156             },
39157             scope: this,
39158             block: true
39159         });
39160     },
39161
39162     anchors : {
39163         "west" : "left",
39164         "east" : "right",
39165         "north" : "top",
39166         "south" : "bottom"
39167     },
39168
39169     sanchors : {
39170         "west" : "l",
39171         "east" : "r",
39172         "north" : "t",
39173         "south" : "b"
39174     },
39175
39176     canchors : {
39177         "west" : "tl-tr",
39178         "east" : "tr-tl",
39179         "north" : "tl-bl",
39180         "south" : "bl-tl"
39181     },
39182
39183     getAnchor : function(){
39184         return this.anchors[this.position];
39185     },
39186
39187     getCollapseAnchor : function(){
39188         return this.canchors[this.position];
39189     },
39190
39191     getSlideAnchor : function(){
39192         return this.sanchors[this.position];
39193     },
39194
39195     getAlignAdj : function(){
39196         var cm = this.cmargins;
39197         switch(this.position){
39198             case "west":
39199                 return [0, 0];
39200             break;
39201             case "east":
39202                 return [0, 0];
39203             break;
39204             case "north":
39205                 return [0, 0];
39206             break;
39207             case "south":
39208                 return [0, 0];
39209             break;
39210         }
39211     },
39212
39213     getExpandAdj : function(){
39214         var c = this.collapsedEl, cm = this.cmargins;
39215         switch(this.position){
39216             case "west":
39217                 return [-(cm.right+c.getWidth()+cm.left), 0];
39218             break;
39219             case "east":
39220                 return [cm.right+c.getWidth()+cm.left, 0];
39221             break;
39222             case "north":
39223                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39224             break;
39225             case "south":
39226                 return [0, cm.top+cm.bottom+c.getHeight()];
39227             break;
39228         }
39229     }
39230 });/*
39231  * Based on:
39232  * Ext JS Library 1.1.1
39233  * Copyright(c) 2006-2007, Ext JS, LLC.
39234  *
39235  * Originally Released Under LGPL - original licence link has changed is not relivant.
39236  *
39237  * Fork - LGPL
39238  * <script type="text/javascript">
39239  */
39240 /*
39241  * These classes are private internal classes
39242  */
39243 Roo.bootstrap.layout.Center = function(config){
39244     config.region = "center";
39245     Roo.bootstrap.layout.Region.call(this, config);
39246     this.visible = true;
39247     this.minWidth = config.minWidth || 20;
39248     this.minHeight = config.minHeight || 20;
39249 };
39250
39251 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39252     hide : function(){
39253         // center panel can't be hidden
39254     },
39255     
39256     show : function(){
39257         // center panel can't be hidden
39258     },
39259     
39260     getMinWidth: function(){
39261         return this.minWidth;
39262     },
39263     
39264     getMinHeight: function(){
39265         return this.minHeight;
39266     }
39267 });
39268
39269
39270
39271
39272  
39273
39274
39275
39276
39277
39278
39279 Roo.bootstrap.layout.North = function(config)
39280 {
39281     config.region = 'north';
39282     config.cursor = 'n-resize';
39283     
39284     Roo.bootstrap.layout.Split.call(this, config);
39285     
39286     
39287     if(this.split){
39288         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39289         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39290         this.split.el.addClass("roo-layout-split-v");
39291     }
39292     //var size = config.initialSize || config.height;
39293     //if(this.el && typeof size != "undefined"){
39294     //    this.el.setHeight(size);
39295     //}
39296 };
39297 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39298 {
39299     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39300      
39301      
39302     onRender : function(ctr, pos)
39303     {
39304         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39305         var size = this.config.initialSize || this.config.height;
39306         if(this.el && typeof size != "undefined"){
39307             this.el.setHeight(size);
39308         }
39309     
39310     },
39311     
39312     getBox : function(){
39313         if(this.collapsed){
39314             return this.collapsedEl.getBox();
39315         }
39316         var box = this.el.getBox();
39317         if(this.split){
39318             box.height += this.split.el.getHeight();
39319         }
39320         return box;
39321     },
39322     
39323     updateBox : function(box){
39324         if(this.split && !this.collapsed){
39325             box.height -= this.split.el.getHeight();
39326             this.split.el.setLeft(box.x);
39327             this.split.el.setTop(box.y+box.height);
39328             this.split.el.setWidth(box.width);
39329         }
39330         if(this.collapsed){
39331             this.updateBody(box.width, null);
39332         }
39333         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39334     }
39335 });
39336
39337
39338
39339
39340
39341 Roo.bootstrap.layout.South = function(config){
39342     config.region = 'south';
39343     config.cursor = 's-resize';
39344     Roo.bootstrap.layout.Split.call(this, config);
39345     if(this.split){
39346         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39347         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39348         this.split.el.addClass("roo-layout-split-v");
39349     }
39350     
39351 };
39352
39353 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39354     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39355     
39356     onRender : function(ctr, pos)
39357     {
39358         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39359         var size = this.config.initialSize || this.config.height;
39360         if(this.el && typeof size != "undefined"){
39361             this.el.setHeight(size);
39362         }
39363     
39364     },
39365     
39366     getBox : function(){
39367         if(this.collapsed){
39368             return this.collapsedEl.getBox();
39369         }
39370         var box = this.el.getBox();
39371         if(this.split){
39372             var sh = this.split.el.getHeight();
39373             box.height += sh;
39374             box.y -= sh;
39375         }
39376         return box;
39377     },
39378     
39379     updateBox : function(box){
39380         if(this.split && !this.collapsed){
39381             var sh = this.split.el.getHeight();
39382             box.height -= sh;
39383             box.y += sh;
39384             this.split.el.setLeft(box.x);
39385             this.split.el.setTop(box.y-sh);
39386             this.split.el.setWidth(box.width);
39387         }
39388         if(this.collapsed){
39389             this.updateBody(box.width, null);
39390         }
39391         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39392     }
39393 });
39394
39395 Roo.bootstrap.layout.East = function(config){
39396     config.region = "east";
39397     config.cursor = "e-resize";
39398     Roo.bootstrap.layout.Split.call(this, config);
39399     if(this.split){
39400         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39401         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39402         this.split.el.addClass("roo-layout-split-h");
39403     }
39404     
39405 };
39406 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39407     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39408     
39409     onRender : function(ctr, pos)
39410     {
39411         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39412         var size = this.config.initialSize || this.config.width;
39413         if(this.el && typeof size != "undefined"){
39414             this.el.setWidth(size);
39415         }
39416     
39417     },
39418     
39419     getBox : function(){
39420         if(this.collapsed){
39421             return this.collapsedEl.getBox();
39422         }
39423         var box = this.el.getBox();
39424         if(this.split){
39425             var sw = this.split.el.getWidth();
39426             box.width += sw;
39427             box.x -= sw;
39428         }
39429         return box;
39430     },
39431
39432     updateBox : function(box){
39433         if(this.split && !this.collapsed){
39434             var sw = this.split.el.getWidth();
39435             box.width -= sw;
39436             this.split.el.setLeft(box.x);
39437             this.split.el.setTop(box.y);
39438             this.split.el.setHeight(box.height);
39439             box.x += sw;
39440         }
39441         if(this.collapsed){
39442             this.updateBody(null, box.height);
39443         }
39444         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39445     }
39446 });
39447
39448 Roo.bootstrap.layout.West = function(config){
39449     config.region = "west";
39450     config.cursor = "w-resize";
39451     
39452     Roo.bootstrap.layout.Split.call(this, config);
39453     if(this.split){
39454         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39455         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39456         this.split.el.addClass("roo-layout-split-h");
39457     }
39458     
39459 };
39460 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39461     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39462     
39463     onRender: function(ctr, pos)
39464     {
39465         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39466         var size = this.config.initialSize || this.config.width;
39467         if(typeof size != "undefined"){
39468             this.el.setWidth(size);
39469         }
39470     },
39471     
39472     getBox : function(){
39473         if(this.collapsed){
39474             return this.collapsedEl.getBox();
39475         }
39476         var box = this.el.getBox();
39477         if (box.width == 0) {
39478             box.width = this.config.width; // kludge?
39479         }
39480         if(this.split){
39481             box.width += this.split.el.getWidth();
39482         }
39483         return box;
39484     },
39485     
39486     updateBox : function(box){
39487         if(this.split && !this.collapsed){
39488             var sw = this.split.el.getWidth();
39489             box.width -= sw;
39490             this.split.el.setLeft(box.x+box.width);
39491             this.split.el.setTop(box.y);
39492             this.split.el.setHeight(box.height);
39493         }
39494         if(this.collapsed){
39495             this.updateBody(null, box.height);
39496         }
39497         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39498     }
39499 });Roo.namespace("Roo.bootstrap.panel");/*
39500  * Based on:
39501  * Ext JS Library 1.1.1
39502  * Copyright(c) 2006-2007, Ext JS, LLC.
39503  *
39504  * Originally Released Under LGPL - original licence link has changed is not relivant.
39505  *
39506  * Fork - LGPL
39507  * <script type="text/javascript">
39508  */
39509 /**
39510  * @class Roo.ContentPanel
39511  * @extends Roo.util.Observable
39512  * A basic ContentPanel element.
39513  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39514  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39515  * @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
39516  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39517  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39518  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39519  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39520  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39521  * @cfg {String} title          The title for this panel
39522  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39523  * @cfg {String} url            Calls {@link #setUrl} with this value
39524  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39525  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39526  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39527  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39528  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39529  * @cfg {Boolean} badges render the badges
39530  * @cfg {String} cls  extra classes to use  
39531  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39532
39533  * @constructor
39534  * Create a new ContentPanel.
39535  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39536  * @param {String/Object} config A string to set only the title or a config object
39537  * @param {String} content (optional) Set the HTML content for this panel
39538  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39539  */
39540 Roo.bootstrap.panel.Content = function( config){
39541     
39542     this.tpl = config.tpl || false;
39543     
39544     var el = config.el;
39545     var content = config.content;
39546
39547     if(config.autoCreate){ // xtype is available if this is called from factory
39548         el = Roo.id();
39549     }
39550     this.el = Roo.get(el);
39551     if(!this.el && config && config.autoCreate){
39552         if(typeof config.autoCreate == "object"){
39553             if(!config.autoCreate.id){
39554                 config.autoCreate.id = config.id||el;
39555             }
39556             this.el = Roo.DomHelper.append(document.body,
39557                         config.autoCreate, true);
39558         }else{
39559             var elcfg =  {
39560                 tag: "div",
39561                 cls: (config.cls || '') +
39562                     (config.background ? ' bg-' + config.background : '') +
39563                     " roo-layout-inactive-content",
39564                 id: config.id||el
39565             };
39566             if (config.iframe) {
39567                 elcfg.cn = [
39568                     {
39569                         tag : 'iframe',
39570                         style : 'border: 0px',
39571                         src : 'about:blank'
39572                     }
39573                 ];
39574             }
39575               
39576             if (config.html) {
39577                 elcfg.html = config.html;
39578                 
39579             }
39580                         
39581             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39582             if (config.iframe) {
39583                 this.iframeEl = this.el.select('iframe',true).first();
39584             }
39585             
39586         }
39587     } 
39588     this.closable = false;
39589     this.loaded = false;
39590     this.active = false;
39591    
39592       
39593     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39594         
39595         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39596         
39597         this.wrapEl = this.el; //this.el.wrap();
39598         var ti = [];
39599         if (config.toolbar.items) {
39600             ti = config.toolbar.items ;
39601             delete config.toolbar.items ;
39602         }
39603         
39604         var nitems = [];
39605         this.toolbar.render(this.wrapEl, 'before');
39606         for(var i =0;i < ti.length;i++) {
39607           //  Roo.log(['add child', items[i]]);
39608             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39609         }
39610         this.toolbar.items = nitems;
39611         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39612         delete config.toolbar;
39613         
39614     }
39615     /*
39616     // xtype created footer. - not sure if will work as we normally have to render first..
39617     if (this.footer && !this.footer.el && this.footer.xtype) {
39618         if (!this.wrapEl) {
39619             this.wrapEl = this.el.wrap();
39620         }
39621     
39622         this.footer.container = this.wrapEl.createChild();
39623          
39624         this.footer = Roo.factory(this.footer, Roo);
39625         
39626     }
39627     */
39628     
39629      if(typeof config == "string"){
39630         this.title = config;
39631     }else{
39632         Roo.apply(this, config);
39633     }
39634     
39635     if(this.resizeEl){
39636         this.resizeEl = Roo.get(this.resizeEl, true);
39637     }else{
39638         this.resizeEl = this.el;
39639     }
39640     // handle view.xtype
39641     
39642  
39643     
39644     
39645     this.addEvents({
39646         /**
39647          * @event activate
39648          * Fires when this panel is activated. 
39649          * @param {Roo.ContentPanel} this
39650          */
39651         "activate" : true,
39652         /**
39653          * @event deactivate
39654          * Fires when this panel is activated. 
39655          * @param {Roo.ContentPanel} this
39656          */
39657         "deactivate" : true,
39658
39659         /**
39660          * @event resize
39661          * Fires when this panel is resized if fitToFrame is true.
39662          * @param {Roo.ContentPanel} this
39663          * @param {Number} width The width after any component adjustments
39664          * @param {Number} height The height after any component adjustments
39665          */
39666         "resize" : true,
39667         
39668          /**
39669          * @event render
39670          * Fires when this tab is created
39671          * @param {Roo.ContentPanel} this
39672          */
39673         "render" : true
39674         
39675         
39676         
39677     });
39678     
39679
39680     
39681     
39682     if(this.autoScroll && !this.iframe){
39683         this.resizeEl.setStyle("overflow", "auto");
39684     } else {
39685         // fix randome scrolling
39686         //this.el.on('scroll', function() {
39687         //    Roo.log('fix random scolling');
39688         //    this.scrollTo('top',0); 
39689         //});
39690     }
39691     content = content || this.content;
39692     if(content){
39693         this.setContent(content);
39694     }
39695     if(config && config.url){
39696         this.setUrl(this.url, this.params, this.loadOnce);
39697     }
39698     
39699     
39700     
39701     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39702     
39703     if (this.view && typeof(this.view.xtype) != 'undefined') {
39704         this.view.el = this.el.appendChild(document.createElement("div"));
39705         this.view = Roo.factory(this.view); 
39706         this.view.render  &&  this.view.render(false, '');  
39707     }
39708     
39709     
39710     this.fireEvent('render', this);
39711 };
39712
39713 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39714     
39715     cls : '',
39716     background : '',
39717     
39718     tabTip : '',
39719     
39720     iframe : false,
39721     iframeEl : false,
39722     
39723     setRegion : function(region){
39724         this.region = region;
39725         this.setActiveClass(region && !this.background);
39726     },
39727     
39728     
39729     setActiveClass: function(state)
39730     {
39731         if(state){
39732            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39733            this.el.setStyle('position','relative');
39734         }else{
39735            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39736            this.el.setStyle('position', 'absolute');
39737         } 
39738     },
39739     
39740     /**
39741      * Returns the toolbar for this Panel if one was configured. 
39742      * @return {Roo.Toolbar} 
39743      */
39744     getToolbar : function(){
39745         return this.toolbar;
39746     },
39747     
39748     setActiveState : function(active)
39749     {
39750         this.active = active;
39751         this.setActiveClass(active);
39752         if(!active){
39753             if(this.fireEvent("deactivate", this) === false){
39754                 return false;
39755             }
39756             return true;
39757         }
39758         this.fireEvent("activate", this);
39759         return true;
39760     },
39761     /**
39762      * Updates this panel's element (not for iframe)
39763      * @param {String} content The new content
39764      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39765     */
39766     setContent : function(content, loadScripts){
39767         if (this.iframe) {
39768             return;
39769         }
39770         
39771         this.el.update(content, loadScripts);
39772     },
39773
39774     ignoreResize : function(w, h){
39775         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39776             return true;
39777         }else{
39778             this.lastSize = {width: w, height: h};
39779             return false;
39780         }
39781     },
39782     /**
39783      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39784      * @return {Roo.UpdateManager} The UpdateManager
39785      */
39786     getUpdateManager : function(){
39787         if (this.iframe) {
39788             return false;
39789         }
39790         return this.el.getUpdateManager();
39791     },
39792      /**
39793      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39794      * Does not work with IFRAME contents
39795      * @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:
39796 <pre><code>
39797 panel.load({
39798     url: "your-url.php",
39799     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39800     callback: yourFunction,
39801     scope: yourObject, //(optional scope)
39802     discardUrl: false,
39803     nocache: false,
39804     text: "Loading...",
39805     timeout: 30,
39806     scripts: false
39807 });
39808 </code></pre>
39809      
39810      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39811      * 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.
39812      * @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}
39813      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39814      * @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.
39815      * @return {Roo.ContentPanel} this
39816      */
39817     load : function(){
39818         
39819         if (this.iframe) {
39820             return this;
39821         }
39822         
39823         var um = this.el.getUpdateManager();
39824         um.update.apply(um, arguments);
39825         return this;
39826     },
39827
39828
39829     /**
39830      * 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.
39831      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39832      * @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)
39833      * @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)
39834      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39835      */
39836     setUrl : function(url, params, loadOnce){
39837         if (this.iframe) {
39838             this.iframeEl.dom.src = url;
39839             return false;
39840         }
39841         
39842         if(this.refreshDelegate){
39843             this.removeListener("activate", this.refreshDelegate);
39844         }
39845         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39846         this.on("activate", this.refreshDelegate);
39847         return this.el.getUpdateManager();
39848     },
39849     
39850     _handleRefresh : function(url, params, loadOnce){
39851         if(!loadOnce || !this.loaded){
39852             var updater = this.el.getUpdateManager();
39853             updater.update(url, params, this._setLoaded.createDelegate(this));
39854         }
39855     },
39856     
39857     _setLoaded : function(){
39858         this.loaded = true;
39859     }, 
39860     
39861     /**
39862      * Returns this panel's id
39863      * @return {String} 
39864      */
39865     getId : function(){
39866         return this.el.id;
39867     },
39868     
39869     /** 
39870      * Returns this panel's element - used by regiosn to add.
39871      * @return {Roo.Element} 
39872      */
39873     getEl : function(){
39874         return this.wrapEl || this.el;
39875     },
39876     
39877    
39878     
39879     adjustForComponents : function(width, height)
39880     {
39881         //Roo.log('adjustForComponents ');
39882         if(this.resizeEl != this.el){
39883             width -= this.el.getFrameWidth('lr');
39884             height -= this.el.getFrameWidth('tb');
39885         }
39886         if(this.toolbar){
39887             var te = this.toolbar.getEl();
39888             te.setWidth(width);
39889             height -= te.getHeight();
39890         }
39891         if(this.footer){
39892             var te = this.footer.getEl();
39893             te.setWidth(width);
39894             height -= te.getHeight();
39895         }
39896         
39897         
39898         if(this.adjustments){
39899             width += this.adjustments[0];
39900             height += this.adjustments[1];
39901         }
39902         return {"width": width, "height": height};
39903     },
39904     
39905     setSize : function(width, height){
39906         if(this.fitToFrame && !this.ignoreResize(width, height)){
39907             if(this.fitContainer && this.resizeEl != this.el){
39908                 this.el.setSize(width, height);
39909             }
39910             var size = this.adjustForComponents(width, height);
39911             if (this.iframe) {
39912                 this.iframeEl.setSize(width,height);
39913             }
39914             
39915             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39916             this.fireEvent('resize', this, size.width, size.height);
39917             
39918             
39919         }
39920     },
39921     
39922     /**
39923      * Returns this panel's title
39924      * @return {String} 
39925      */
39926     getTitle : function(){
39927         
39928         if (typeof(this.title) != 'object') {
39929             return this.title;
39930         }
39931         
39932         var t = '';
39933         for (var k in this.title) {
39934             if (!this.title.hasOwnProperty(k)) {
39935                 continue;
39936             }
39937             
39938             if (k.indexOf('-') >= 0) {
39939                 var s = k.split('-');
39940                 for (var i = 0; i<s.length; i++) {
39941                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39942                 }
39943             } else {
39944                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39945             }
39946         }
39947         return t;
39948     },
39949     
39950     /**
39951      * Set this panel's title
39952      * @param {String} title
39953      */
39954     setTitle : function(title){
39955         this.title = title;
39956         if(this.region){
39957             this.region.updatePanelTitle(this, title);
39958         }
39959     },
39960     
39961     /**
39962      * Returns true is this panel was configured to be closable
39963      * @return {Boolean} 
39964      */
39965     isClosable : function(){
39966         return this.closable;
39967     },
39968     
39969     beforeSlide : function(){
39970         this.el.clip();
39971         this.resizeEl.clip();
39972     },
39973     
39974     afterSlide : function(){
39975         this.el.unclip();
39976         this.resizeEl.unclip();
39977     },
39978     
39979     /**
39980      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39981      *   Will fail silently if the {@link #setUrl} method has not been called.
39982      *   This does not activate the panel, just updates its content.
39983      */
39984     refresh : function(){
39985         if(this.refreshDelegate){
39986            this.loaded = false;
39987            this.refreshDelegate();
39988         }
39989     },
39990     
39991     /**
39992      * Destroys this panel
39993      */
39994     destroy : function(){
39995         this.el.removeAllListeners();
39996         var tempEl = document.createElement("span");
39997         tempEl.appendChild(this.el.dom);
39998         tempEl.innerHTML = "";
39999         this.el.remove();
40000         this.el = null;
40001     },
40002     
40003     /**
40004      * form - if the content panel contains a form - this is a reference to it.
40005      * @type {Roo.form.Form}
40006      */
40007     form : false,
40008     /**
40009      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40010      *    This contains a reference to it.
40011      * @type {Roo.View}
40012      */
40013     view : false,
40014     
40015       /**
40016      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40017      * <pre><code>
40018
40019 layout.addxtype({
40020        xtype : 'Form',
40021        items: [ .... ]
40022    }
40023 );
40024
40025 </code></pre>
40026      * @param {Object} cfg Xtype definition of item to add.
40027      */
40028     
40029     
40030     getChildContainer: function () {
40031         return this.getEl();
40032     }
40033     
40034     
40035     /*
40036         var  ret = new Roo.factory(cfg);
40037         return ret;
40038         
40039         
40040         // add form..
40041         if (cfg.xtype.match(/^Form$/)) {
40042             
40043             var el;
40044             //if (this.footer) {
40045             //    el = this.footer.container.insertSibling(false, 'before');
40046             //} else {
40047                 el = this.el.createChild();
40048             //}
40049
40050             this.form = new  Roo.form.Form(cfg);
40051             
40052             
40053             if ( this.form.allItems.length) {
40054                 this.form.render(el.dom);
40055             }
40056             return this.form;
40057         }
40058         // should only have one of theses..
40059         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40060             // views.. should not be just added - used named prop 'view''
40061             
40062             cfg.el = this.el.appendChild(document.createElement("div"));
40063             // factory?
40064             
40065             var ret = new Roo.factory(cfg);
40066              
40067              ret.render && ret.render(false, ''); // render blank..
40068             this.view = ret;
40069             return ret;
40070         }
40071         return false;
40072     }
40073     \*/
40074 });
40075  
40076 /**
40077  * @class Roo.bootstrap.panel.Grid
40078  * @extends Roo.bootstrap.panel.Content
40079  * @constructor
40080  * Create a new GridPanel.
40081  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40082  * @param {Object} config A the config object
40083   
40084  */
40085
40086
40087
40088 Roo.bootstrap.panel.Grid = function(config)
40089 {
40090     
40091       
40092     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40093         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40094
40095     config.el = this.wrapper;
40096     //this.el = this.wrapper;
40097     
40098       if (config.container) {
40099         // ctor'ed from a Border/panel.grid
40100         
40101         
40102         this.wrapper.setStyle("overflow", "hidden");
40103         this.wrapper.addClass('roo-grid-container');
40104
40105     }
40106     
40107     
40108     if(config.toolbar){
40109         var tool_el = this.wrapper.createChild();    
40110         this.toolbar = Roo.factory(config.toolbar);
40111         var ti = [];
40112         if (config.toolbar.items) {
40113             ti = config.toolbar.items ;
40114             delete config.toolbar.items ;
40115         }
40116         
40117         var nitems = [];
40118         this.toolbar.render(tool_el);
40119         for(var i =0;i < ti.length;i++) {
40120           //  Roo.log(['add child', items[i]]);
40121             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40122         }
40123         this.toolbar.items = nitems;
40124         
40125         delete config.toolbar;
40126     }
40127     
40128     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40129     config.grid.scrollBody = true;;
40130     config.grid.monitorWindowResize = false; // turn off autosizing
40131     config.grid.autoHeight = false;
40132     config.grid.autoWidth = false;
40133     
40134     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40135     
40136     if (config.background) {
40137         // render grid on panel activation (if panel background)
40138         this.on('activate', function(gp) {
40139             if (!gp.grid.rendered) {
40140                 gp.grid.render(this.wrapper);
40141                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40142             }
40143         });
40144             
40145     } else {
40146         this.grid.render(this.wrapper);
40147         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40148
40149     }
40150     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40151     // ??? needed ??? config.el = this.wrapper;
40152     
40153     
40154     
40155   
40156     // xtype created footer. - not sure if will work as we normally have to render first..
40157     if (this.footer && !this.footer.el && this.footer.xtype) {
40158         
40159         var ctr = this.grid.getView().getFooterPanel(true);
40160         this.footer.dataSource = this.grid.dataSource;
40161         this.footer = Roo.factory(this.footer, Roo);
40162         this.footer.render(ctr);
40163         
40164     }
40165     
40166     
40167     
40168     
40169      
40170 };
40171
40172 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40173     getId : function(){
40174         return this.grid.id;
40175     },
40176     
40177     /**
40178      * Returns the grid for this panel
40179      * @return {Roo.bootstrap.Table} 
40180      */
40181     getGrid : function(){
40182         return this.grid;    
40183     },
40184     
40185     setSize : function(width, height){
40186         if(!this.ignoreResize(width, height)){
40187             var grid = this.grid;
40188             var size = this.adjustForComponents(width, height);
40189             // tfoot is not a footer?
40190           
40191             
40192             var gridel = grid.getGridEl();
40193             gridel.setSize(size.width, size.height);
40194             
40195             var tbd = grid.getGridEl().select('tbody', true).first();
40196             var thd = grid.getGridEl().select('thead',true).first();
40197             var tbf= grid.getGridEl().select('tfoot', true).first();
40198
40199             if (tbf) {
40200                 size.height -= tbf.getHeight();
40201             }
40202             if (thd) {
40203                 size.height -= thd.getHeight();
40204             }
40205             
40206             tbd.setSize(size.width, size.height );
40207             // this is for the account management tab -seems to work there.
40208             var thd = grid.getGridEl().select('thead',true).first();
40209             //if (tbd) {
40210             //    tbd.setSize(size.width, size.height - thd.getHeight());
40211             //}
40212              
40213             grid.autoSize();
40214         }
40215     },
40216      
40217     
40218     
40219     beforeSlide : function(){
40220         this.grid.getView().scroller.clip();
40221     },
40222     
40223     afterSlide : function(){
40224         this.grid.getView().scroller.unclip();
40225     },
40226     
40227     destroy : function(){
40228         this.grid.destroy();
40229         delete this.grid;
40230         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40231     }
40232 });
40233
40234 /**
40235  * @class Roo.bootstrap.panel.Nest
40236  * @extends Roo.bootstrap.panel.Content
40237  * @constructor
40238  * Create a new Panel, that can contain a layout.Border.
40239  * 
40240  * 
40241  * @param {Roo.BorderLayout} layout The layout for this panel
40242  * @param {String/Object} config A string to set only the title or a config object
40243  */
40244 Roo.bootstrap.panel.Nest = function(config)
40245 {
40246     // construct with only one argument..
40247     /* FIXME - implement nicer consturctors
40248     if (layout.layout) {
40249         config = layout;
40250         layout = config.layout;
40251         delete config.layout;
40252     }
40253     if (layout.xtype && !layout.getEl) {
40254         // then layout needs constructing..
40255         layout = Roo.factory(layout, Roo);
40256     }
40257     */
40258     
40259     config.el =  config.layout.getEl();
40260     
40261     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40262     
40263     config.layout.monitorWindowResize = false; // turn off autosizing
40264     this.layout = config.layout;
40265     this.layout.getEl().addClass("roo-layout-nested-layout");
40266     this.layout.parent = this;
40267     
40268     
40269     
40270     
40271 };
40272
40273 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40274
40275     setSize : function(width, height){
40276         if(!this.ignoreResize(width, height)){
40277             var size = this.adjustForComponents(width, height);
40278             var el = this.layout.getEl();
40279             if (size.height < 1) {
40280                 el.setWidth(size.width);   
40281             } else {
40282                 el.setSize(size.width, size.height);
40283             }
40284             var touch = el.dom.offsetWidth;
40285             this.layout.layout();
40286             // ie requires a double layout on the first pass
40287             if(Roo.isIE && !this.initialized){
40288                 this.initialized = true;
40289                 this.layout.layout();
40290             }
40291         }
40292     },
40293     
40294     // activate all subpanels if not currently active..
40295     
40296     setActiveState : function(active){
40297         this.active = active;
40298         this.setActiveClass(active);
40299         
40300         if(!active){
40301             this.fireEvent("deactivate", this);
40302             return;
40303         }
40304         
40305         this.fireEvent("activate", this);
40306         // not sure if this should happen before or after..
40307         if (!this.layout) {
40308             return; // should not happen..
40309         }
40310         var reg = false;
40311         for (var r in this.layout.regions) {
40312             reg = this.layout.getRegion(r);
40313             if (reg.getActivePanel()) {
40314                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40315                 reg.setActivePanel(reg.getActivePanel());
40316                 continue;
40317             }
40318             if (!reg.panels.length) {
40319                 continue;
40320             }
40321             reg.showPanel(reg.getPanel(0));
40322         }
40323         
40324         
40325         
40326         
40327     },
40328     
40329     /**
40330      * Returns the nested BorderLayout for this panel
40331      * @return {Roo.BorderLayout} 
40332      */
40333     getLayout : function(){
40334         return this.layout;
40335     },
40336     
40337      /**
40338      * Adds a xtype elements to the layout of the nested panel
40339      * <pre><code>
40340
40341 panel.addxtype({
40342        xtype : 'ContentPanel',
40343        region: 'west',
40344        items: [ .... ]
40345    }
40346 );
40347
40348 panel.addxtype({
40349         xtype : 'NestedLayoutPanel',
40350         region: 'west',
40351         layout: {
40352            center: { },
40353            west: { }   
40354         },
40355         items : [ ... list of content panels or nested layout panels.. ]
40356    }
40357 );
40358 </code></pre>
40359      * @param {Object} cfg Xtype definition of item to add.
40360      */
40361     addxtype : function(cfg) {
40362         return this.layout.addxtype(cfg);
40363     
40364     }
40365 });/*
40366  * Based on:
40367  * Ext JS Library 1.1.1
40368  * Copyright(c) 2006-2007, Ext JS, LLC.
40369  *
40370  * Originally Released Under LGPL - original licence link has changed is not relivant.
40371  *
40372  * Fork - LGPL
40373  * <script type="text/javascript">
40374  */
40375 /**
40376  * @class Roo.TabPanel
40377  * @extends Roo.util.Observable
40378  * A lightweight tab container.
40379  * <br><br>
40380  * Usage:
40381  * <pre><code>
40382 // basic tabs 1, built from existing content
40383 var tabs = new Roo.TabPanel("tabs1");
40384 tabs.addTab("script", "View Script");
40385 tabs.addTab("markup", "View Markup");
40386 tabs.activate("script");
40387
40388 // more advanced tabs, built from javascript
40389 var jtabs = new Roo.TabPanel("jtabs");
40390 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40391
40392 // set up the UpdateManager
40393 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40394 var updater = tab2.getUpdateManager();
40395 updater.setDefaultUrl("ajax1.htm");
40396 tab2.on('activate', updater.refresh, updater, true);
40397
40398 // Use setUrl for Ajax loading
40399 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40400 tab3.setUrl("ajax2.htm", null, true);
40401
40402 // Disabled tab
40403 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40404 tab4.disable();
40405
40406 jtabs.activate("jtabs-1");
40407  * </code></pre>
40408  * @constructor
40409  * Create a new TabPanel.
40410  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40411  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40412  */
40413 Roo.bootstrap.panel.Tabs = function(config){
40414     /**
40415     * The container element for this TabPanel.
40416     * @type Roo.Element
40417     */
40418     this.el = Roo.get(config.el);
40419     delete config.el;
40420     if(config){
40421         if(typeof config == "boolean"){
40422             this.tabPosition = config ? "bottom" : "top";
40423         }else{
40424             Roo.apply(this, config);
40425         }
40426     }
40427     
40428     if(this.tabPosition == "bottom"){
40429         // if tabs are at the bottom = create the body first.
40430         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40431         this.el.addClass("roo-tabs-bottom");
40432     }
40433     // next create the tabs holders
40434     
40435     if (this.tabPosition == "west"){
40436         
40437         var reg = this.region; // fake it..
40438         while (reg) {
40439             if (!reg.mgr.parent) {
40440                 break;
40441             }
40442             reg = reg.mgr.parent.region;
40443         }
40444         Roo.log("got nest?");
40445         Roo.log(reg);
40446         if (reg.mgr.getRegion('west')) {
40447             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40448             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40449             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40450             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40451             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40452         
40453             
40454         }
40455         
40456         
40457     } else {
40458      
40459         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40460         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40461         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40462         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40463     }
40464     
40465     
40466     if(Roo.isIE){
40467         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40468     }
40469     
40470     // finally - if tabs are at the top, then create the body last..
40471     if(this.tabPosition != "bottom"){
40472         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40473          * @type Roo.Element
40474          */
40475         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40476         this.el.addClass("roo-tabs-top");
40477     }
40478     this.items = [];
40479
40480     this.bodyEl.setStyle("position", "relative");
40481
40482     this.active = null;
40483     this.activateDelegate = this.activate.createDelegate(this);
40484
40485     this.addEvents({
40486         /**
40487          * @event tabchange
40488          * Fires when the active tab changes
40489          * @param {Roo.TabPanel} this
40490          * @param {Roo.TabPanelItem} activePanel The new active tab
40491          */
40492         "tabchange": true,
40493         /**
40494          * @event beforetabchange
40495          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40496          * @param {Roo.TabPanel} this
40497          * @param {Object} e Set cancel to true on this object to cancel the tab change
40498          * @param {Roo.TabPanelItem} tab The tab being changed to
40499          */
40500         "beforetabchange" : true
40501     });
40502
40503     Roo.EventManager.onWindowResize(this.onResize, this);
40504     this.cpad = this.el.getPadding("lr");
40505     this.hiddenCount = 0;
40506
40507
40508     // toolbar on the tabbar support...
40509     if (this.toolbar) {
40510         alert("no toolbar support yet");
40511         this.toolbar  = false;
40512         /*
40513         var tcfg = this.toolbar;
40514         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40515         this.toolbar = new Roo.Toolbar(tcfg);
40516         if (Roo.isSafari) {
40517             var tbl = tcfg.container.child('table', true);
40518             tbl.setAttribute('width', '100%');
40519         }
40520         */
40521         
40522     }
40523    
40524
40525
40526     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40527 };
40528
40529 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40530     /*
40531      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40532      */
40533     tabPosition : "top",
40534     /*
40535      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40536      */
40537     currentTabWidth : 0,
40538     /*
40539      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40540      */
40541     minTabWidth : 40,
40542     /*
40543      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40544      */
40545     maxTabWidth : 250,
40546     /*
40547      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40548      */
40549     preferredTabWidth : 175,
40550     /*
40551      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40552      */
40553     resizeTabs : false,
40554     /*
40555      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40556      */
40557     monitorResize : true,
40558     /*
40559      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40560      */
40561     toolbar : false,  // set by caller..
40562     
40563     region : false, /// set by caller
40564     
40565     disableTooltips : true, // not used yet...
40566
40567     /**
40568      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40569      * @param {String} id The id of the div to use <b>or create</b>
40570      * @param {String} text The text for the tab
40571      * @param {String} content (optional) Content to put in the TabPanelItem body
40572      * @param {Boolean} closable (optional) True to create a close icon on the tab
40573      * @return {Roo.TabPanelItem} The created TabPanelItem
40574      */
40575     addTab : function(id, text, content, closable, tpl)
40576     {
40577         var item = new Roo.bootstrap.panel.TabItem({
40578             panel: this,
40579             id : id,
40580             text : text,
40581             closable : closable,
40582             tpl : tpl
40583         });
40584         this.addTabItem(item);
40585         if(content){
40586             item.setContent(content);
40587         }
40588         return item;
40589     },
40590
40591     /**
40592      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40593      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40594      * @return {Roo.TabPanelItem}
40595      */
40596     getTab : function(id){
40597         return this.items[id];
40598     },
40599
40600     /**
40601      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40602      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40603      */
40604     hideTab : function(id){
40605         var t = this.items[id];
40606         if(!t.isHidden()){
40607            t.setHidden(true);
40608            this.hiddenCount++;
40609            this.autoSizeTabs();
40610         }
40611     },
40612
40613     /**
40614      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40615      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40616      */
40617     unhideTab : function(id){
40618         var t = this.items[id];
40619         if(t.isHidden()){
40620            t.setHidden(false);
40621            this.hiddenCount--;
40622            this.autoSizeTabs();
40623         }
40624     },
40625
40626     /**
40627      * Adds an existing {@link Roo.TabPanelItem}.
40628      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40629      */
40630     addTabItem : function(item)
40631     {
40632         this.items[item.id] = item;
40633         this.items.push(item);
40634         this.autoSizeTabs();
40635       //  if(this.resizeTabs){
40636     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40637   //         this.autoSizeTabs();
40638 //        }else{
40639 //            item.autoSize();
40640        // }
40641     },
40642
40643     /**
40644      * Removes a {@link Roo.TabPanelItem}.
40645      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40646      */
40647     removeTab : function(id){
40648         var items = this.items;
40649         var tab = items[id];
40650         if(!tab) { return; }
40651         var index = items.indexOf(tab);
40652         if(this.active == tab && items.length > 1){
40653             var newTab = this.getNextAvailable(index);
40654             if(newTab) {
40655                 newTab.activate();
40656             }
40657         }
40658         this.stripEl.dom.removeChild(tab.pnode.dom);
40659         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40660             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40661         }
40662         items.splice(index, 1);
40663         delete this.items[tab.id];
40664         tab.fireEvent("close", tab);
40665         tab.purgeListeners();
40666         this.autoSizeTabs();
40667     },
40668
40669     getNextAvailable : function(start){
40670         var items = this.items;
40671         var index = start;
40672         // look for a next tab that will slide over to
40673         // replace the one being removed
40674         while(index < items.length){
40675             var item = items[++index];
40676             if(item && !item.isHidden()){
40677                 return item;
40678             }
40679         }
40680         // if one isn't found select the previous tab (on the left)
40681         index = start;
40682         while(index >= 0){
40683             var item = items[--index];
40684             if(item && !item.isHidden()){
40685                 return item;
40686             }
40687         }
40688         return null;
40689     },
40690
40691     /**
40692      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40693      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40694      */
40695     disableTab : function(id){
40696         var tab = this.items[id];
40697         if(tab && this.active != tab){
40698             tab.disable();
40699         }
40700     },
40701
40702     /**
40703      * Enables a {@link Roo.TabPanelItem} that is disabled.
40704      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40705      */
40706     enableTab : function(id){
40707         var tab = this.items[id];
40708         tab.enable();
40709     },
40710
40711     /**
40712      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40713      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40714      * @return {Roo.TabPanelItem} The TabPanelItem.
40715      */
40716     activate : function(id)
40717     {
40718         //Roo.log('activite:'  + id);
40719         
40720         var tab = this.items[id];
40721         if(!tab){
40722             return null;
40723         }
40724         if(tab == this.active || tab.disabled){
40725             return tab;
40726         }
40727         var e = {};
40728         this.fireEvent("beforetabchange", this, e, tab);
40729         if(e.cancel !== true && !tab.disabled){
40730             if(this.active){
40731                 this.active.hide();
40732             }
40733             this.active = this.items[id];
40734             this.active.show();
40735             this.fireEvent("tabchange", this, this.active);
40736         }
40737         return tab;
40738     },
40739
40740     /**
40741      * Gets the active {@link Roo.TabPanelItem}.
40742      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40743      */
40744     getActiveTab : function(){
40745         return this.active;
40746     },
40747
40748     /**
40749      * Updates the tab body element to fit the height of the container element
40750      * for overflow scrolling
40751      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40752      */
40753     syncHeight : function(targetHeight){
40754         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40755         var bm = this.bodyEl.getMargins();
40756         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40757         this.bodyEl.setHeight(newHeight);
40758         return newHeight;
40759     },
40760
40761     onResize : function(){
40762         if(this.monitorResize){
40763             this.autoSizeTabs();
40764         }
40765     },
40766
40767     /**
40768      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40769      */
40770     beginUpdate : function(){
40771         this.updating = true;
40772     },
40773
40774     /**
40775      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40776      */
40777     endUpdate : function(){
40778         this.updating = false;
40779         this.autoSizeTabs();
40780     },
40781
40782     /**
40783      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40784      */
40785     autoSizeTabs : function()
40786     {
40787         var count = this.items.length;
40788         var vcount = count - this.hiddenCount;
40789         
40790         if (vcount < 2) {
40791             this.stripEl.hide();
40792         } else {
40793             this.stripEl.show();
40794         }
40795         
40796         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40797             return;
40798         }
40799         
40800         
40801         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40802         var availWidth = Math.floor(w / vcount);
40803         var b = this.stripBody;
40804         if(b.getWidth() > w){
40805             var tabs = this.items;
40806             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40807             if(availWidth < this.minTabWidth){
40808                 /*if(!this.sleft){    // incomplete scrolling code
40809                     this.createScrollButtons();
40810                 }
40811                 this.showScroll();
40812                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40813             }
40814         }else{
40815             if(this.currentTabWidth < this.preferredTabWidth){
40816                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40817             }
40818         }
40819     },
40820
40821     /**
40822      * Returns the number of tabs in this TabPanel.
40823      * @return {Number}
40824      */
40825      getCount : function(){
40826          return this.items.length;
40827      },
40828
40829     /**
40830      * Resizes all the tabs to the passed width
40831      * @param {Number} The new width
40832      */
40833     setTabWidth : function(width){
40834         this.currentTabWidth = width;
40835         for(var i = 0, len = this.items.length; i < len; i++) {
40836                 if(!this.items[i].isHidden()) {
40837                 this.items[i].setWidth(width);
40838             }
40839         }
40840     },
40841
40842     /**
40843      * Destroys this TabPanel
40844      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40845      */
40846     destroy : function(removeEl){
40847         Roo.EventManager.removeResizeListener(this.onResize, this);
40848         for(var i = 0, len = this.items.length; i < len; i++){
40849             this.items[i].purgeListeners();
40850         }
40851         if(removeEl === true){
40852             this.el.update("");
40853             this.el.remove();
40854         }
40855     },
40856     
40857     createStrip : function(container)
40858     {
40859         var strip = document.createElement("nav");
40860         strip.className = Roo.bootstrap.version == 4 ?
40861             "navbar-light bg-light" : 
40862             "navbar navbar-default"; //"x-tabs-wrap";
40863         container.appendChild(strip);
40864         return strip;
40865     },
40866     
40867     createStripList : function(strip)
40868     {
40869         // div wrapper for retard IE
40870         // returns the "tr" element.
40871         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40872         //'<div class="x-tabs-strip-wrap">'+
40873           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40874           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40875         return strip.firstChild; //.firstChild.firstChild.firstChild;
40876     },
40877     createBody : function(container)
40878     {
40879         var body = document.createElement("div");
40880         Roo.id(body, "tab-body");
40881         //Roo.fly(body).addClass("x-tabs-body");
40882         Roo.fly(body).addClass("tab-content");
40883         container.appendChild(body);
40884         return body;
40885     },
40886     createItemBody :function(bodyEl, id){
40887         var body = Roo.getDom(id);
40888         if(!body){
40889             body = document.createElement("div");
40890             body.id = id;
40891         }
40892         //Roo.fly(body).addClass("x-tabs-item-body");
40893         Roo.fly(body).addClass("tab-pane");
40894          bodyEl.insertBefore(body, bodyEl.firstChild);
40895         return body;
40896     },
40897     /** @private */
40898     createStripElements :  function(stripEl, text, closable, tpl)
40899     {
40900         var td = document.createElement("li"); // was td..
40901         td.className = 'nav-item';
40902         
40903         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40904         
40905         
40906         stripEl.appendChild(td);
40907         /*if(closable){
40908             td.className = "x-tabs-closable";
40909             if(!this.closeTpl){
40910                 this.closeTpl = new Roo.Template(
40911                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40912                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40913                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40914                 );
40915             }
40916             var el = this.closeTpl.overwrite(td, {"text": text});
40917             var close = el.getElementsByTagName("div")[0];
40918             var inner = el.getElementsByTagName("em")[0];
40919             return {"el": el, "close": close, "inner": inner};
40920         } else {
40921         */
40922         // not sure what this is..
40923 //            if(!this.tabTpl){
40924                 //this.tabTpl = new Roo.Template(
40925                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40926                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40927                 //);
40928 //                this.tabTpl = new Roo.Template(
40929 //                   '<a href="#">' +
40930 //                   '<span unselectable="on"' +
40931 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40932 //                            ' >{text}</span></a>'
40933 //                );
40934 //                
40935 //            }
40936
40937
40938             var template = tpl || this.tabTpl || false;
40939             
40940             if(!template){
40941                 template =  new Roo.Template(
40942                         Roo.bootstrap.version == 4 ? 
40943                             (
40944                                 '<a class="nav-link" href="#" unselectable="on"' +
40945                                      (this.disableTooltips ? '' : ' title="{text}"') +
40946                                      ' >{text}</a>'
40947                             ) : (
40948                                 '<a class="nav-link" href="#">' +
40949                                 '<span unselectable="on"' +
40950                                          (this.disableTooltips ? '' : ' title="{text}"') +
40951                                     ' >{text}</span></a>'
40952                             )
40953                 );
40954             }
40955             
40956             switch (typeof(template)) {
40957                 case 'object' :
40958                     break;
40959                 case 'string' :
40960                     template = new Roo.Template(template);
40961                     break;
40962                 default :
40963                     break;
40964             }
40965             
40966             var el = template.overwrite(td, {"text": text});
40967             
40968             var inner = el.getElementsByTagName("span")[0];
40969             
40970             return {"el": el, "inner": inner};
40971             
40972     }
40973         
40974     
40975 });
40976
40977 /**
40978  * @class Roo.TabPanelItem
40979  * @extends Roo.util.Observable
40980  * Represents an individual item (tab plus body) in a TabPanel.
40981  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40982  * @param {String} id The id of this TabPanelItem
40983  * @param {String} text The text for the tab of this TabPanelItem
40984  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40985  */
40986 Roo.bootstrap.panel.TabItem = function(config){
40987     /**
40988      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40989      * @type Roo.TabPanel
40990      */
40991     this.tabPanel = config.panel;
40992     /**
40993      * The id for this TabPanelItem
40994      * @type String
40995      */
40996     this.id = config.id;
40997     /** @private */
40998     this.disabled = false;
40999     /** @private */
41000     this.text = config.text;
41001     /** @private */
41002     this.loaded = false;
41003     this.closable = config.closable;
41004
41005     /**
41006      * The body element for this TabPanelItem.
41007      * @type Roo.Element
41008      */
41009     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41010     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41011     this.bodyEl.setStyle("display", "block");
41012     this.bodyEl.setStyle("zoom", "1");
41013     //this.hideAction();
41014
41015     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41016     /** @private */
41017     this.el = Roo.get(els.el);
41018     this.inner = Roo.get(els.inner, true);
41019      this.textEl = Roo.bootstrap.version == 4 ?
41020         this.el : Roo.get(this.el.dom.firstChild, true);
41021
41022     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41023     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41024
41025     
41026 //    this.el.on("mousedown", this.onTabMouseDown, this);
41027     this.el.on("click", this.onTabClick, this);
41028     /** @private */
41029     if(config.closable){
41030         var c = Roo.get(els.close, true);
41031         c.dom.title = this.closeText;
41032         c.addClassOnOver("close-over");
41033         c.on("click", this.closeClick, this);
41034      }
41035
41036     this.addEvents({
41037          /**
41038          * @event activate
41039          * Fires when this tab becomes the active tab.
41040          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41041          * @param {Roo.TabPanelItem} this
41042          */
41043         "activate": true,
41044         /**
41045          * @event beforeclose
41046          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41047          * @param {Roo.TabPanelItem} this
41048          * @param {Object} e Set cancel to true on this object to cancel the close.
41049          */
41050         "beforeclose": true,
41051         /**
41052          * @event close
41053          * Fires when this tab is closed.
41054          * @param {Roo.TabPanelItem} this
41055          */
41056          "close": true,
41057         /**
41058          * @event deactivate
41059          * Fires when this tab is no longer the active tab.
41060          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41061          * @param {Roo.TabPanelItem} this
41062          */
41063          "deactivate" : true
41064     });
41065     this.hidden = false;
41066
41067     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41068 };
41069
41070 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41071            {
41072     purgeListeners : function(){
41073        Roo.util.Observable.prototype.purgeListeners.call(this);
41074        this.el.removeAllListeners();
41075     },
41076     /**
41077      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41078      */
41079     show : function(){
41080         this.status_node.addClass("active");
41081         this.showAction();
41082         if(Roo.isOpera){
41083             this.tabPanel.stripWrap.repaint();
41084         }
41085         this.fireEvent("activate", this.tabPanel, this);
41086     },
41087
41088     /**
41089      * Returns true if this tab is the active tab.
41090      * @return {Boolean}
41091      */
41092     isActive : function(){
41093         return this.tabPanel.getActiveTab() == this;
41094     },
41095
41096     /**
41097      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41098      */
41099     hide : function(){
41100         this.status_node.removeClass("active");
41101         this.hideAction();
41102         this.fireEvent("deactivate", this.tabPanel, this);
41103     },
41104
41105     hideAction : function(){
41106         this.bodyEl.hide();
41107         this.bodyEl.setStyle("position", "absolute");
41108         this.bodyEl.setLeft("-20000px");
41109         this.bodyEl.setTop("-20000px");
41110     },
41111
41112     showAction : function(){
41113         this.bodyEl.setStyle("position", "relative");
41114         this.bodyEl.setTop("");
41115         this.bodyEl.setLeft("");
41116         this.bodyEl.show();
41117     },
41118
41119     /**
41120      * Set the tooltip for the tab.
41121      * @param {String} tooltip The tab's tooltip
41122      */
41123     setTooltip : function(text){
41124         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41125             this.textEl.dom.qtip = text;
41126             this.textEl.dom.removeAttribute('title');
41127         }else{
41128             this.textEl.dom.title = text;
41129         }
41130     },
41131
41132     onTabClick : function(e){
41133         e.preventDefault();
41134         this.tabPanel.activate(this.id);
41135     },
41136
41137     onTabMouseDown : function(e){
41138         e.preventDefault();
41139         this.tabPanel.activate(this.id);
41140     },
41141 /*
41142     getWidth : function(){
41143         return this.inner.getWidth();
41144     },
41145
41146     setWidth : function(width){
41147         var iwidth = width - this.linode.getPadding("lr");
41148         this.inner.setWidth(iwidth);
41149         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41150         this.linode.setWidth(width);
41151     },
41152 */
41153     /**
41154      * Show or hide the tab
41155      * @param {Boolean} hidden True to hide or false to show.
41156      */
41157     setHidden : function(hidden){
41158         this.hidden = hidden;
41159         this.linode.setStyle("display", hidden ? "none" : "");
41160     },
41161
41162     /**
41163      * Returns true if this tab is "hidden"
41164      * @return {Boolean}
41165      */
41166     isHidden : function(){
41167         return this.hidden;
41168     },
41169
41170     /**
41171      * Returns the text for this tab
41172      * @return {String}
41173      */
41174     getText : function(){
41175         return this.text;
41176     },
41177     /*
41178     autoSize : function(){
41179         //this.el.beginMeasure();
41180         this.textEl.setWidth(1);
41181         /*
41182          *  #2804 [new] Tabs in Roojs
41183          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41184          */
41185         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41186         //this.el.endMeasure();
41187     //},
41188
41189     /**
41190      * Sets the text for the tab (Note: this also sets the tooltip text)
41191      * @param {String} text The tab's text and tooltip
41192      */
41193     setText : function(text){
41194         this.text = text;
41195         this.textEl.update(text);
41196         this.setTooltip(text);
41197         //if(!this.tabPanel.resizeTabs){
41198         //    this.autoSize();
41199         //}
41200     },
41201     /**
41202      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41203      */
41204     activate : function(){
41205         this.tabPanel.activate(this.id);
41206     },
41207
41208     /**
41209      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41210      */
41211     disable : function(){
41212         if(this.tabPanel.active != this){
41213             this.disabled = true;
41214             this.status_node.addClass("disabled");
41215         }
41216     },
41217
41218     /**
41219      * Enables this TabPanelItem if it was previously disabled.
41220      */
41221     enable : function(){
41222         this.disabled = false;
41223         this.status_node.removeClass("disabled");
41224     },
41225
41226     /**
41227      * Sets the content for this TabPanelItem.
41228      * @param {String} content The content
41229      * @param {Boolean} loadScripts true to look for and load scripts
41230      */
41231     setContent : function(content, loadScripts){
41232         this.bodyEl.update(content, loadScripts);
41233     },
41234
41235     /**
41236      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41237      * @return {Roo.UpdateManager} The UpdateManager
41238      */
41239     getUpdateManager : function(){
41240         return this.bodyEl.getUpdateManager();
41241     },
41242
41243     /**
41244      * Set a URL to be used to load the content for this TabPanelItem.
41245      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41246      * @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)
41247      * @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)
41248      * @return {Roo.UpdateManager} The UpdateManager
41249      */
41250     setUrl : function(url, params, loadOnce){
41251         if(this.refreshDelegate){
41252             this.un('activate', this.refreshDelegate);
41253         }
41254         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41255         this.on("activate", this.refreshDelegate);
41256         return this.bodyEl.getUpdateManager();
41257     },
41258
41259     /** @private */
41260     _handleRefresh : function(url, params, loadOnce){
41261         if(!loadOnce || !this.loaded){
41262             var updater = this.bodyEl.getUpdateManager();
41263             updater.update(url, params, this._setLoaded.createDelegate(this));
41264         }
41265     },
41266
41267     /**
41268      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41269      *   Will fail silently if the setUrl method has not been called.
41270      *   This does not activate the panel, just updates its content.
41271      */
41272     refresh : function(){
41273         if(this.refreshDelegate){
41274            this.loaded = false;
41275            this.refreshDelegate();
41276         }
41277     },
41278
41279     /** @private */
41280     _setLoaded : function(){
41281         this.loaded = true;
41282     },
41283
41284     /** @private */
41285     closeClick : function(e){
41286         var o = {};
41287         e.stopEvent();
41288         this.fireEvent("beforeclose", this, o);
41289         if(o.cancel !== true){
41290             this.tabPanel.removeTab(this.id);
41291         }
41292     },
41293     /**
41294      * The text displayed in the tooltip for the close icon.
41295      * @type String
41296      */
41297     closeText : "Close this tab"
41298 });
41299 /**
41300 *    This script refer to:
41301 *    Title: International Telephone Input
41302 *    Author: Jack O'Connor
41303 *    Code version:  v12.1.12
41304 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41305 **/
41306
41307 Roo.bootstrap.PhoneInputData = function() {
41308     var d = [
41309       [
41310         "Afghanistan (‫افغانستان‬‎)",
41311         "af",
41312         "93"
41313       ],
41314       [
41315         "Albania (Shqipëri)",
41316         "al",
41317         "355"
41318       ],
41319       [
41320         "Algeria (‫الجزائر‬‎)",
41321         "dz",
41322         "213"
41323       ],
41324       [
41325         "American Samoa",
41326         "as",
41327         "1684"
41328       ],
41329       [
41330         "Andorra",
41331         "ad",
41332         "376"
41333       ],
41334       [
41335         "Angola",
41336         "ao",
41337         "244"
41338       ],
41339       [
41340         "Anguilla",
41341         "ai",
41342         "1264"
41343       ],
41344       [
41345         "Antigua and Barbuda",
41346         "ag",
41347         "1268"
41348       ],
41349       [
41350         "Argentina",
41351         "ar",
41352         "54"
41353       ],
41354       [
41355         "Armenia (Հայաստան)",
41356         "am",
41357         "374"
41358       ],
41359       [
41360         "Aruba",
41361         "aw",
41362         "297"
41363       ],
41364       [
41365         "Australia",
41366         "au",
41367         "61",
41368         0
41369       ],
41370       [
41371         "Austria (Österreich)",
41372         "at",
41373         "43"
41374       ],
41375       [
41376         "Azerbaijan (Azərbaycan)",
41377         "az",
41378         "994"
41379       ],
41380       [
41381         "Bahamas",
41382         "bs",
41383         "1242"
41384       ],
41385       [
41386         "Bahrain (‫البحرين‬‎)",
41387         "bh",
41388         "973"
41389       ],
41390       [
41391         "Bangladesh (বাংলাদেশ)",
41392         "bd",
41393         "880"
41394       ],
41395       [
41396         "Barbados",
41397         "bb",
41398         "1246"
41399       ],
41400       [
41401         "Belarus (Беларусь)",
41402         "by",
41403         "375"
41404       ],
41405       [
41406         "Belgium (België)",
41407         "be",
41408         "32"
41409       ],
41410       [
41411         "Belize",
41412         "bz",
41413         "501"
41414       ],
41415       [
41416         "Benin (Bénin)",
41417         "bj",
41418         "229"
41419       ],
41420       [
41421         "Bermuda",
41422         "bm",
41423         "1441"
41424       ],
41425       [
41426         "Bhutan (འབྲུག)",
41427         "bt",
41428         "975"
41429       ],
41430       [
41431         "Bolivia",
41432         "bo",
41433         "591"
41434       ],
41435       [
41436         "Bosnia and Herzegovina (Босна и Херцеговина)",
41437         "ba",
41438         "387"
41439       ],
41440       [
41441         "Botswana",
41442         "bw",
41443         "267"
41444       ],
41445       [
41446         "Brazil (Brasil)",
41447         "br",
41448         "55"
41449       ],
41450       [
41451         "British Indian Ocean Territory",
41452         "io",
41453         "246"
41454       ],
41455       [
41456         "British Virgin Islands",
41457         "vg",
41458         "1284"
41459       ],
41460       [
41461         "Brunei",
41462         "bn",
41463         "673"
41464       ],
41465       [
41466         "Bulgaria (България)",
41467         "bg",
41468         "359"
41469       ],
41470       [
41471         "Burkina Faso",
41472         "bf",
41473         "226"
41474       ],
41475       [
41476         "Burundi (Uburundi)",
41477         "bi",
41478         "257"
41479       ],
41480       [
41481         "Cambodia (កម្ពុជា)",
41482         "kh",
41483         "855"
41484       ],
41485       [
41486         "Cameroon (Cameroun)",
41487         "cm",
41488         "237"
41489       ],
41490       [
41491         "Canada",
41492         "ca",
41493         "1",
41494         1,
41495         ["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"]
41496       ],
41497       [
41498         "Cape Verde (Kabu Verdi)",
41499         "cv",
41500         "238"
41501       ],
41502       [
41503         "Caribbean Netherlands",
41504         "bq",
41505         "599",
41506         1
41507       ],
41508       [
41509         "Cayman Islands",
41510         "ky",
41511         "1345"
41512       ],
41513       [
41514         "Central African Republic (République centrafricaine)",
41515         "cf",
41516         "236"
41517       ],
41518       [
41519         "Chad (Tchad)",
41520         "td",
41521         "235"
41522       ],
41523       [
41524         "Chile",
41525         "cl",
41526         "56"
41527       ],
41528       [
41529         "China (中国)",
41530         "cn",
41531         "86"
41532       ],
41533       [
41534         "Christmas Island",
41535         "cx",
41536         "61",
41537         2
41538       ],
41539       [
41540         "Cocos (Keeling) Islands",
41541         "cc",
41542         "61",
41543         1
41544       ],
41545       [
41546         "Colombia",
41547         "co",
41548         "57"
41549       ],
41550       [
41551         "Comoros (‫جزر القمر‬‎)",
41552         "km",
41553         "269"
41554       ],
41555       [
41556         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41557         "cd",
41558         "243"
41559       ],
41560       [
41561         "Congo (Republic) (Congo-Brazzaville)",
41562         "cg",
41563         "242"
41564       ],
41565       [
41566         "Cook Islands",
41567         "ck",
41568         "682"
41569       ],
41570       [
41571         "Costa Rica",
41572         "cr",
41573         "506"
41574       ],
41575       [
41576         "Côte d’Ivoire",
41577         "ci",
41578         "225"
41579       ],
41580       [
41581         "Croatia (Hrvatska)",
41582         "hr",
41583         "385"
41584       ],
41585       [
41586         "Cuba",
41587         "cu",
41588         "53"
41589       ],
41590       [
41591         "Curaçao",
41592         "cw",
41593         "599",
41594         0
41595       ],
41596       [
41597         "Cyprus (Κύπρος)",
41598         "cy",
41599         "357"
41600       ],
41601       [
41602         "Czech Republic (Česká republika)",
41603         "cz",
41604         "420"
41605       ],
41606       [
41607         "Denmark (Danmark)",
41608         "dk",
41609         "45"
41610       ],
41611       [
41612         "Djibouti",
41613         "dj",
41614         "253"
41615       ],
41616       [
41617         "Dominica",
41618         "dm",
41619         "1767"
41620       ],
41621       [
41622         "Dominican Republic (República Dominicana)",
41623         "do",
41624         "1",
41625         2,
41626         ["809", "829", "849"]
41627       ],
41628       [
41629         "Ecuador",
41630         "ec",
41631         "593"
41632       ],
41633       [
41634         "Egypt (‫مصر‬‎)",
41635         "eg",
41636         "20"
41637       ],
41638       [
41639         "El Salvador",
41640         "sv",
41641         "503"
41642       ],
41643       [
41644         "Equatorial Guinea (Guinea Ecuatorial)",
41645         "gq",
41646         "240"
41647       ],
41648       [
41649         "Eritrea",
41650         "er",
41651         "291"
41652       ],
41653       [
41654         "Estonia (Eesti)",
41655         "ee",
41656         "372"
41657       ],
41658       [
41659         "Ethiopia",
41660         "et",
41661         "251"
41662       ],
41663       [
41664         "Falkland Islands (Islas Malvinas)",
41665         "fk",
41666         "500"
41667       ],
41668       [
41669         "Faroe Islands (Føroyar)",
41670         "fo",
41671         "298"
41672       ],
41673       [
41674         "Fiji",
41675         "fj",
41676         "679"
41677       ],
41678       [
41679         "Finland (Suomi)",
41680         "fi",
41681         "358",
41682         0
41683       ],
41684       [
41685         "France",
41686         "fr",
41687         "33"
41688       ],
41689       [
41690         "French Guiana (Guyane française)",
41691         "gf",
41692         "594"
41693       ],
41694       [
41695         "French Polynesia (Polynésie française)",
41696         "pf",
41697         "689"
41698       ],
41699       [
41700         "Gabon",
41701         "ga",
41702         "241"
41703       ],
41704       [
41705         "Gambia",
41706         "gm",
41707         "220"
41708       ],
41709       [
41710         "Georgia (საქართველო)",
41711         "ge",
41712         "995"
41713       ],
41714       [
41715         "Germany (Deutschland)",
41716         "de",
41717         "49"
41718       ],
41719       [
41720         "Ghana (Gaana)",
41721         "gh",
41722         "233"
41723       ],
41724       [
41725         "Gibraltar",
41726         "gi",
41727         "350"
41728       ],
41729       [
41730         "Greece (Ελλάδα)",
41731         "gr",
41732         "30"
41733       ],
41734       [
41735         "Greenland (Kalaallit Nunaat)",
41736         "gl",
41737         "299"
41738       ],
41739       [
41740         "Grenada",
41741         "gd",
41742         "1473"
41743       ],
41744       [
41745         "Guadeloupe",
41746         "gp",
41747         "590",
41748         0
41749       ],
41750       [
41751         "Guam",
41752         "gu",
41753         "1671"
41754       ],
41755       [
41756         "Guatemala",
41757         "gt",
41758         "502"
41759       ],
41760       [
41761         "Guernsey",
41762         "gg",
41763         "44",
41764         1
41765       ],
41766       [
41767         "Guinea (Guinée)",
41768         "gn",
41769         "224"
41770       ],
41771       [
41772         "Guinea-Bissau (Guiné Bissau)",
41773         "gw",
41774         "245"
41775       ],
41776       [
41777         "Guyana",
41778         "gy",
41779         "592"
41780       ],
41781       [
41782         "Haiti",
41783         "ht",
41784         "509"
41785       ],
41786       [
41787         "Honduras",
41788         "hn",
41789         "504"
41790       ],
41791       [
41792         "Hong Kong (香港)",
41793         "hk",
41794         "852"
41795       ],
41796       [
41797         "Hungary (Magyarország)",
41798         "hu",
41799         "36"
41800       ],
41801       [
41802         "Iceland (Ísland)",
41803         "is",
41804         "354"
41805       ],
41806       [
41807         "India (भारत)",
41808         "in",
41809         "91"
41810       ],
41811       [
41812         "Indonesia",
41813         "id",
41814         "62"
41815       ],
41816       [
41817         "Iran (‫ایران‬‎)",
41818         "ir",
41819         "98"
41820       ],
41821       [
41822         "Iraq (‫العراق‬‎)",
41823         "iq",
41824         "964"
41825       ],
41826       [
41827         "Ireland",
41828         "ie",
41829         "353"
41830       ],
41831       [
41832         "Isle of Man",
41833         "im",
41834         "44",
41835         2
41836       ],
41837       [
41838         "Israel (‫ישראל‬‎)",
41839         "il",
41840         "972"
41841       ],
41842       [
41843         "Italy (Italia)",
41844         "it",
41845         "39",
41846         0
41847       ],
41848       [
41849         "Jamaica",
41850         "jm",
41851         "1876"
41852       ],
41853       [
41854         "Japan (日本)",
41855         "jp",
41856         "81"
41857       ],
41858       [
41859         "Jersey",
41860         "je",
41861         "44",
41862         3
41863       ],
41864       [
41865         "Jordan (‫الأردن‬‎)",
41866         "jo",
41867         "962"
41868       ],
41869       [
41870         "Kazakhstan (Казахстан)",
41871         "kz",
41872         "7",
41873         1
41874       ],
41875       [
41876         "Kenya",
41877         "ke",
41878         "254"
41879       ],
41880       [
41881         "Kiribati",
41882         "ki",
41883         "686"
41884       ],
41885       [
41886         "Kosovo",
41887         "xk",
41888         "383"
41889       ],
41890       [
41891         "Kuwait (‫الكويت‬‎)",
41892         "kw",
41893         "965"
41894       ],
41895       [
41896         "Kyrgyzstan (Кыргызстан)",
41897         "kg",
41898         "996"
41899       ],
41900       [
41901         "Laos (ລາວ)",
41902         "la",
41903         "856"
41904       ],
41905       [
41906         "Latvia (Latvija)",
41907         "lv",
41908         "371"
41909       ],
41910       [
41911         "Lebanon (‫لبنان‬‎)",
41912         "lb",
41913         "961"
41914       ],
41915       [
41916         "Lesotho",
41917         "ls",
41918         "266"
41919       ],
41920       [
41921         "Liberia",
41922         "lr",
41923         "231"
41924       ],
41925       [
41926         "Libya (‫ليبيا‬‎)",
41927         "ly",
41928         "218"
41929       ],
41930       [
41931         "Liechtenstein",
41932         "li",
41933         "423"
41934       ],
41935       [
41936         "Lithuania (Lietuva)",
41937         "lt",
41938         "370"
41939       ],
41940       [
41941         "Luxembourg",
41942         "lu",
41943         "352"
41944       ],
41945       [
41946         "Macau (澳門)",
41947         "mo",
41948         "853"
41949       ],
41950       [
41951         "Macedonia (FYROM) (Македонија)",
41952         "mk",
41953         "389"
41954       ],
41955       [
41956         "Madagascar (Madagasikara)",
41957         "mg",
41958         "261"
41959       ],
41960       [
41961         "Malawi",
41962         "mw",
41963         "265"
41964       ],
41965       [
41966         "Malaysia",
41967         "my",
41968         "60"
41969       ],
41970       [
41971         "Maldives",
41972         "mv",
41973         "960"
41974       ],
41975       [
41976         "Mali",
41977         "ml",
41978         "223"
41979       ],
41980       [
41981         "Malta",
41982         "mt",
41983         "356"
41984       ],
41985       [
41986         "Marshall Islands",
41987         "mh",
41988         "692"
41989       ],
41990       [
41991         "Martinique",
41992         "mq",
41993         "596"
41994       ],
41995       [
41996         "Mauritania (‫موريتانيا‬‎)",
41997         "mr",
41998         "222"
41999       ],
42000       [
42001         "Mauritius (Moris)",
42002         "mu",
42003         "230"
42004       ],
42005       [
42006         "Mayotte",
42007         "yt",
42008         "262",
42009         1
42010       ],
42011       [
42012         "Mexico (México)",
42013         "mx",
42014         "52"
42015       ],
42016       [
42017         "Micronesia",
42018         "fm",
42019         "691"
42020       ],
42021       [
42022         "Moldova (Republica Moldova)",
42023         "md",
42024         "373"
42025       ],
42026       [
42027         "Monaco",
42028         "mc",
42029         "377"
42030       ],
42031       [
42032         "Mongolia (Монгол)",
42033         "mn",
42034         "976"
42035       ],
42036       [
42037         "Montenegro (Crna Gora)",
42038         "me",
42039         "382"
42040       ],
42041       [
42042         "Montserrat",
42043         "ms",
42044         "1664"
42045       ],
42046       [
42047         "Morocco (‫المغرب‬‎)",
42048         "ma",
42049         "212",
42050         0
42051       ],
42052       [
42053         "Mozambique (Moçambique)",
42054         "mz",
42055         "258"
42056       ],
42057       [
42058         "Myanmar (Burma) (မြန်မာ)",
42059         "mm",
42060         "95"
42061       ],
42062       [
42063         "Namibia (Namibië)",
42064         "na",
42065         "264"
42066       ],
42067       [
42068         "Nauru",
42069         "nr",
42070         "674"
42071       ],
42072       [
42073         "Nepal (नेपाल)",
42074         "np",
42075         "977"
42076       ],
42077       [
42078         "Netherlands (Nederland)",
42079         "nl",
42080         "31"
42081       ],
42082       [
42083         "New Caledonia (Nouvelle-Calédonie)",
42084         "nc",
42085         "687"
42086       ],
42087       [
42088         "New Zealand",
42089         "nz",
42090         "64"
42091       ],
42092       [
42093         "Nicaragua",
42094         "ni",
42095         "505"
42096       ],
42097       [
42098         "Niger (Nijar)",
42099         "ne",
42100         "227"
42101       ],
42102       [
42103         "Nigeria",
42104         "ng",
42105         "234"
42106       ],
42107       [
42108         "Niue",
42109         "nu",
42110         "683"
42111       ],
42112       [
42113         "Norfolk Island",
42114         "nf",
42115         "672"
42116       ],
42117       [
42118         "North Korea (조선 민주주의 인민 공화국)",
42119         "kp",
42120         "850"
42121       ],
42122       [
42123         "Northern Mariana Islands",
42124         "mp",
42125         "1670"
42126       ],
42127       [
42128         "Norway (Norge)",
42129         "no",
42130         "47",
42131         0
42132       ],
42133       [
42134         "Oman (‫عُمان‬‎)",
42135         "om",
42136         "968"
42137       ],
42138       [
42139         "Pakistan (‫پاکستان‬‎)",
42140         "pk",
42141         "92"
42142       ],
42143       [
42144         "Palau",
42145         "pw",
42146         "680"
42147       ],
42148       [
42149         "Palestine (‫فلسطين‬‎)",
42150         "ps",
42151         "970"
42152       ],
42153       [
42154         "Panama (Panamá)",
42155         "pa",
42156         "507"
42157       ],
42158       [
42159         "Papua New Guinea",
42160         "pg",
42161         "675"
42162       ],
42163       [
42164         "Paraguay",
42165         "py",
42166         "595"
42167       ],
42168       [
42169         "Peru (Perú)",
42170         "pe",
42171         "51"
42172       ],
42173       [
42174         "Philippines",
42175         "ph",
42176         "63"
42177       ],
42178       [
42179         "Poland (Polska)",
42180         "pl",
42181         "48"
42182       ],
42183       [
42184         "Portugal",
42185         "pt",
42186         "351"
42187       ],
42188       [
42189         "Puerto Rico",
42190         "pr",
42191         "1",
42192         3,
42193         ["787", "939"]
42194       ],
42195       [
42196         "Qatar (‫قطر‬‎)",
42197         "qa",
42198         "974"
42199       ],
42200       [
42201         "Réunion (La Réunion)",
42202         "re",
42203         "262",
42204         0
42205       ],
42206       [
42207         "Romania (România)",
42208         "ro",
42209         "40"
42210       ],
42211       [
42212         "Russia (Россия)",
42213         "ru",
42214         "7",
42215         0
42216       ],
42217       [
42218         "Rwanda",
42219         "rw",
42220         "250"
42221       ],
42222       [
42223         "Saint Barthélemy",
42224         "bl",
42225         "590",
42226         1
42227       ],
42228       [
42229         "Saint Helena",
42230         "sh",
42231         "290"
42232       ],
42233       [
42234         "Saint Kitts and Nevis",
42235         "kn",
42236         "1869"
42237       ],
42238       [
42239         "Saint Lucia",
42240         "lc",
42241         "1758"
42242       ],
42243       [
42244         "Saint Martin (Saint-Martin (partie française))",
42245         "mf",
42246         "590",
42247         2
42248       ],
42249       [
42250         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42251         "pm",
42252         "508"
42253       ],
42254       [
42255         "Saint Vincent and the Grenadines",
42256         "vc",
42257         "1784"
42258       ],
42259       [
42260         "Samoa",
42261         "ws",
42262         "685"
42263       ],
42264       [
42265         "San Marino",
42266         "sm",
42267         "378"
42268       ],
42269       [
42270         "São Tomé and Príncipe (São Tomé e Príncipe)",
42271         "st",
42272         "239"
42273       ],
42274       [
42275         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42276         "sa",
42277         "966"
42278       ],
42279       [
42280         "Senegal (Sénégal)",
42281         "sn",
42282         "221"
42283       ],
42284       [
42285         "Serbia (Србија)",
42286         "rs",
42287         "381"
42288       ],
42289       [
42290         "Seychelles",
42291         "sc",
42292         "248"
42293       ],
42294       [
42295         "Sierra Leone",
42296         "sl",
42297         "232"
42298       ],
42299       [
42300         "Singapore",
42301         "sg",
42302         "65"
42303       ],
42304       [
42305         "Sint Maarten",
42306         "sx",
42307         "1721"
42308       ],
42309       [
42310         "Slovakia (Slovensko)",
42311         "sk",
42312         "421"
42313       ],
42314       [
42315         "Slovenia (Slovenija)",
42316         "si",
42317         "386"
42318       ],
42319       [
42320         "Solomon Islands",
42321         "sb",
42322         "677"
42323       ],
42324       [
42325         "Somalia (Soomaaliya)",
42326         "so",
42327         "252"
42328       ],
42329       [
42330         "South Africa",
42331         "za",
42332         "27"
42333       ],
42334       [
42335         "South Korea (대한민국)",
42336         "kr",
42337         "82"
42338       ],
42339       [
42340         "South Sudan (‫جنوب السودان‬‎)",
42341         "ss",
42342         "211"
42343       ],
42344       [
42345         "Spain (España)",
42346         "es",
42347         "34"
42348       ],
42349       [
42350         "Sri Lanka (ශ්‍රී ලංකාව)",
42351         "lk",
42352         "94"
42353       ],
42354       [
42355         "Sudan (‫السودان‬‎)",
42356         "sd",
42357         "249"
42358       ],
42359       [
42360         "Suriname",
42361         "sr",
42362         "597"
42363       ],
42364       [
42365         "Svalbard and Jan Mayen",
42366         "sj",
42367         "47",
42368         1
42369       ],
42370       [
42371         "Swaziland",
42372         "sz",
42373         "268"
42374       ],
42375       [
42376         "Sweden (Sverige)",
42377         "se",
42378         "46"
42379       ],
42380       [
42381         "Switzerland (Schweiz)",
42382         "ch",
42383         "41"
42384       ],
42385       [
42386         "Syria (‫سوريا‬‎)",
42387         "sy",
42388         "963"
42389       ],
42390       [
42391         "Taiwan (台灣)",
42392         "tw",
42393         "886"
42394       ],
42395       [
42396         "Tajikistan",
42397         "tj",
42398         "992"
42399       ],
42400       [
42401         "Tanzania",
42402         "tz",
42403         "255"
42404       ],
42405       [
42406         "Thailand (ไทย)",
42407         "th",
42408         "66"
42409       ],
42410       [
42411         "Timor-Leste",
42412         "tl",
42413         "670"
42414       ],
42415       [
42416         "Togo",
42417         "tg",
42418         "228"
42419       ],
42420       [
42421         "Tokelau",
42422         "tk",
42423         "690"
42424       ],
42425       [
42426         "Tonga",
42427         "to",
42428         "676"
42429       ],
42430       [
42431         "Trinidad and Tobago",
42432         "tt",
42433         "1868"
42434       ],
42435       [
42436         "Tunisia (‫تونس‬‎)",
42437         "tn",
42438         "216"
42439       ],
42440       [
42441         "Turkey (Türkiye)",
42442         "tr",
42443         "90"
42444       ],
42445       [
42446         "Turkmenistan",
42447         "tm",
42448         "993"
42449       ],
42450       [
42451         "Turks and Caicos Islands",
42452         "tc",
42453         "1649"
42454       ],
42455       [
42456         "Tuvalu",
42457         "tv",
42458         "688"
42459       ],
42460       [
42461         "U.S. Virgin Islands",
42462         "vi",
42463         "1340"
42464       ],
42465       [
42466         "Uganda",
42467         "ug",
42468         "256"
42469       ],
42470       [
42471         "Ukraine (Україна)",
42472         "ua",
42473         "380"
42474       ],
42475       [
42476         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42477         "ae",
42478         "971"
42479       ],
42480       [
42481         "United Kingdom",
42482         "gb",
42483         "44",
42484         0
42485       ],
42486       [
42487         "United States",
42488         "us",
42489         "1",
42490         0
42491       ],
42492       [
42493         "Uruguay",
42494         "uy",
42495         "598"
42496       ],
42497       [
42498         "Uzbekistan (Oʻzbekiston)",
42499         "uz",
42500         "998"
42501       ],
42502       [
42503         "Vanuatu",
42504         "vu",
42505         "678"
42506       ],
42507       [
42508         "Vatican City (Città del Vaticano)",
42509         "va",
42510         "39",
42511         1
42512       ],
42513       [
42514         "Venezuela",
42515         "ve",
42516         "58"
42517       ],
42518       [
42519         "Vietnam (Việt Nam)",
42520         "vn",
42521         "84"
42522       ],
42523       [
42524         "Wallis and Futuna (Wallis-et-Futuna)",
42525         "wf",
42526         "681"
42527       ],
42528       [
42529         "Western Sahara (‫الصحراء الغربية‬‎)",
42530         "eh",
42531         "212",
42532         1
42533       ],
42534       [
42535         "Yemen (‫اليمن‬‎)",
42536         "ye",
42537         "967"
42538       ],
42539       [
42540         "Zambia",
42541         "zm",
42542         "260"
42543       ],
42544       [
42545         "Zimbabwe",
42546         "zw",
42547         "263"
42548       ],
42549       [
42550         "Åland Islands",
42551         "ax",
42552         "358",
42553         1
42554       ]
42555   ];
42556   
42557   return d;
42558 }/**
42559 *    This script refer to:
42560 *    Title: International Telephone Input
42561 *    Author: Jack O'Connor
42562 *    Code version:  v12.1.12
42563 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42564 **/
42565
42566 /**
42567  * @class Roo.bootstrap.PhoneInput
42568  * @extends Roo.bootstrap.TriggerField
42569  * An input with International dial-code selection
42570  
42571  * @cfg {String} defaultDialCode default '+852'
42572  * @cfg {Array} preferedCountries default []
42573   
42574  * @constructor
42575  * Create a new PhoneInput.
42576  * @param {Object} config Configuration options
42577  */
42578
42579 Roo.bootstrap.PhoneInput = function(config) {
42580     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42581 };
42582
42583 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42584         
42585         listWidth: undefined,
42586         
42587         selectedClass: 'active',
42588         
42589         invalidClass : "has-warning",
42590         
42591         validClass: 'has-success',
42592         
42593         allowed: '0123456789',
42594         
42595         max_length: 15,
42596         
42597         /**
42598          * @cfg {String} defaultDialCode The default dial code when initializing the input
42599          */
42600         defaultDialCode: '+852',
42601         
42602         /**
42603          * @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
42604          */
42605         preferedCountries: false,
42606         
42607         getAutoCreate : function()
42608         {
42609             var data = Roo.bootstrap.PhoneInputData();
42610             var align = this.labelAlign || this.parentLabelAlign();
42611             var id = Roo.id();
42612             
42613             this.allCountries = [];
42614             this.dialCodeMapping = [];
42615             
42616             for (var i = 0; i < data.length; i++) {
42617               var c = data[i];
42618               this.allCountries[i] = {
42619                 name: c[0],
42620                 iso2: c[1],
42621                 dialCode: c[2],
42622                 priority: c[3] || 0,
42623                 areaCodes: c[4] || null
42624               };
42625               this.dialCodeMapping[c[2]] = {
42626                   name: c[0],
42627                   iso2: c[1],
42628                   priority: c[3] || 0,
42629                   areaCodes: c[4] || null
42630               };
42631             }
42632             
42633             var cfg = {
42634                 cls: 'form-group',
42635                 cn: []
42636             };
42637             
42638             var input =  {
42639                 tag: 'input',
42640                 id : id,
42641                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42642                 maxlength: this.max_length,
42643                 cls : 'form-control tel-input',
42644                 autocomplete: 'new-password'
42645             };
42646             
42647             var hiddenInput = {
42648                 tag: 'input',
42649                 type: 'hidden',
42650                 cls: 'hidden-tel-input'
42651             };
42652             
42653             if (this.name) {
42654                 hiddenInput.name = this.name;
42655             }
42656             
42657             if (this.disabled) {
42658                 input.disabled = true;
42659             }
42660             
42661             var flag_container = {
42662                 tag: 'div',
42663                 cls: 'flag-box',
42664                 cn: [
42665                     {
42666                         tag: 'div',
42667                         cls: 'flag'
42668                     },
42669                     {
42670                         tag: 'div',
42671                         cls: 'caret'
42672                     }
42673                 ]
42674             };
42675             
42676             var box = {
42677                 tag: 'div',
42678                 cls: this.hasFeedback ? 'has-feedback' : '',
42679                 cn: [
42680                     hiddenInput,
42681                     input,
42682                     {
42683                         tag: 'input',
42684                         cls: 'dial-code-holder',
42685                         disabled: true
42686                     }
42687                 ]
42688             };
42689             
42690             var container = {
42691                 cls: 'roo-select2-container input-group',
42692                 cn: [
42693                     flag_container,
42694                     box
42695                 ]
42696             };
42697             
42698             if (this.fieldLabel.length) {
42699                 var indicator = {
42700                     tag: 'i',
42701                     tooltip: 'This field is required'
42702                 };
42703                 
42704                 var label = {
42705                     tag: 'label',
42706                     'for':  id,
42707                     cls: 'control-label',
42708                     cn: []
42709                 };
42710                 
42711                 var label_text = {
42712                     tag: 'span',
42713                     html: this.fieldLabel
42714                 };
42715                 
42716                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42717                 label.cn = [
42718                     indicator,
42719                     label_text
42720                 ];
42721                 
42722                 if(this.indicatorpos == 'right') {
42723                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42724                     label.cn = [
42725                         label_text,
42726                         indicator
42727                     ];
42728                 }
42729                 
42730                 if(align == 'left') {
42731                     container = {
42732                         tag: 'div',
42733                         cn: [
42734                             container
42735                         ]
42736                     };
42737                     
42738                     if(this.labelWidth > 12){
42739                         label.style = "width: " + this.labelWidth + 'px';
42740                     }
42741                     if(this.labelWidth < 13 && this.labelmd == 0){
42742                         this.labelmd = this.labelWidth;
42743                     }
42744                     if(this.labellg > 0){
42745                         label.cls += ' col-lg-' + this.labellg;
42746                         input.cls += ' col-lg-' + (12 - this.labellg);
42747                     }
42748                     if(this.labelmd > 0){
42749                         label.cls += ' col-md-' + this.labelmd;
42750                         container.cls += ' col-md-' + (12 - this.labelmd);
42751                     }
42752                     if(this.labelsm > 0){
42753                         label.cls += ' col-sm-' + this.labelsm;
42754                         container.cls += ' col-sm-' + (12 - this.labelsm);
42755                     }
42756                     if(this.labelxs > 0){
42757                         label.cls += ' col-xs-' + this.labelxs;
42758                         container.cls += ' col-xs-' + (12 - this.labelxs);
42759                     }
42760                 }
42761             }
42762             
42763             cfg.cn = [
42764                 label,
42765                 container
42766             ];
42767             
42768             var settings = this;
42769             
42770             ['xs','sm','md','lg'].map(function(size){
42771                 if (settings[size]) {
42772                     cfg.cls += ' col-' + size + '-' + settings[size];
42773                 }
42774             });
42775             
42776             this.store = new Roo.data.Store({
42777                 proxy : new Roo.data.MemoryProxy({}),
42778                 reader : new Roo.data.JsonReader({
42779                     fields : [
42780                         {
42781                             'name' : 'name',
42782                             'type' : 'string'
42783                         },
42784                         {
42785                             'name' : 'iso2',
42786                             'type' : 'string'
42787                         },
42788                         {
42789                             'name' : 'dialCode',
42790                             'type' : 'string'
42791                         },
42792                         {
42793                             'name' : 'priority',
42794                             'type' : 'string'
42795                         },
42796                         {
42797                             'name' : 'areaCodes',
42798                             'type' : 'string'
42799                         }
42800                     ]
42801                 })
42802             });
42803             
42804             if(!this.preferedCountries) {
42805                 this.preferedCountries = [
42806                     'hk',
42807                     'gb',
42808                     'us'
42809                 ];
42810             }
42811             
42812             var p = this.preferedCountries.reverse();
42813             
42814             if(p) {
42815                 for (var i = 0; i < p.length; i++) {
42816                     for (var j = 0; j < this.allCountries.length; j++) {
42817                         if(this.allCountries[j].iso2 == p[i]) {
42818                             var t = this.allCountries[j];
42819                             this.allCountries.splice(j,1);
42820                             this.allCountries.unshift(t);
42821                         }
42822                     } 
42823                 }
42824             }
42825             
42826             this.store.proxy.data = {
42827                 success: true,
42828                 data: this.allCountries
42829             };
42830             
42831             return cfg;
42832         },
42833         
42834         initEvents : function()
42835         {
42836             this.createList();
42837             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42838             
42839             this.indicator = this.indicatorEl();
42840             this.flag = this.flagEl();
42841             this.dialCodeHolder = this.dialCodeHolderEl();
42842             
42843             this.trigger = this.el.select('div.flag-box',true).first();
42844             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42845             
42846             var _this = this;
42847             
42848             (function(){
42849                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42850                 _this.list.setWidth(lw);
42851             }).defer(100);
42852             
42853             this.list.on('mouseover', this.onViewOver, this);
42854             this.list.on('mousemove', this.onViewMove, this);
42855             this.inputEl().on("keyup", this.onKeyUp, this);
42856             this.inputEl().on("keypress", this.onKeyPress, this);
42857             
42858             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42859
42860             this.view = new Roo.View(this.list, this.tpl, {
42861                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42862             });
42863             
42864             this.view.on('click', this.onViewClick, this);
42865             this.setValue(this.defaultDialCode);
42866         },
42867         
42868         onTriggerClick : function(e)
42869         {
42870             Roo.log('trigger click');
42871             if(this.disabled){
42872                 return;
42873             }
42874             
42875             if(this.isExpanded()){
42876                 this.collapse();
42877                 this.hasFocus = false;
42878             }else {
42879                 this.store.load({});
42880                 this.hasFocus = true;
42881                 this.expand();
42882             }
42883         },
42884         
42885         isExpanded : function()
42886         {
42887             return this.list.isVisible();
42888         },
42889         
42890         collapse : function()
42891         {
42892             if(!this.isExpanded()){
42893                 return;
42894             }
42895             this.list.hide();
42896             Roo.get(document).un('mousedown', this.collapseIf, this);
42897             Roo.get(document).un('mousewheel', this.collapseIf, this);
42898             this.fireEvent('collapse', this);
42899             this.validate();
42900         },
42901         
42902         expand : function()
42903         {
42904             Roo.log('expand');
42905
42906             if(this.isExpanded() || !this.hasFocus){
42907                 return;
42908             }
42909             
42910             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42911             this.list.setWidth(lw);
42912             
42913             this.list.show();
42914             this.restrictHeight();
42915             
42916             Roo.get(document).on('mousedown', this.collapseIf, this);
42917             Roo.get(document).on('mousewheel', this.collapseIf, this);
42918             
42919             this.fireEvent('expand', this);
42920         },
42921         
42922         restrictHeight : function()
42923         {
42924             this.list.alignTo(this.inputEl(), this.listAlign);
42925             this.list.alignTo(this.inputEl(), this.listAlign);
42926         },
42927         
42928         onViewOver : function(e, t)
42929         {
42930             if(this.inKeyMode){
42931                 return;
42932             }
42933             var item = this.view.findItemFromChild(t);
42934             
42935             if(item){
42936                 var index = this.view.indexOf(item);
42937                 this.select(index, false);
42938             }
42939         },
42940
42941         // private
42942         onViewClick : function(view, doFocus, el, e)
42943         {
42944             var index = this.view.getSelectedIndexes()[0];
42945             
42946             var r = this.store.getAt(index);
42947             
42948             if(r){
42949                 this.onSelect(r, index);
42950             }
42951             if(doFocus !== false && !this.blockFocus){
42952                 this.inputEl().focus();
42953             }
42954         },
42955         
42956         onViewMove : function(e, t)
42957         {
42958             this.inKeyMode = false;
42959         },
42960         
42961         select : function(index, scrollIntoView)
42962         {
42963             this.selectedIndex = index;
42964             this.view.select(index);
42965             if(scrollIntoView !== false){
42966                 var el = this.view.getNode(index);
42967                 if(el){
42968                     this.list.scrollChildIntoView(el, false);
42969                 }
42970             }
42971         },
42972         
42973         createList : function()
42974         {
42975             this.list = Roo.get(document.body).createChild({
42976                 tag: 'ul',
42977                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42978                 style: 'display:none'
42979             });
42980             
42981             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42982         },
42983         
42984         collapseIf : function(e)
42985         {
42986             var in_combo  = e.within(this.el);
42987             var in_list =  e.within(this.list);
42988             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42989             
42990             if (in_combo || in_list || is_list) {
42991                 return;
42992             }
42993             this.collapse();
42994         },
42995         
42996         onSelect : function(record, index)
42997         {
42998             if(this.fireEvent('beforeselect', this, record, index) !== false){
42999                 
43000                 this.setFlagClass(record.data.iso2);
43001                 this.setDialCode(record.data.dialCode);
43002                 this.hasFocus = false;
43003                 this.collapse();
43004                 this.fireEvent('select', this, record, index);
43005             }
43006         },
43007         
43008         flagEl : function()
43009         {
43010             var flag = this.el.select('div.flag',true).first();
43011             if(!flag){
43012                 return false;
43013             }
43014             return flag;
43015         },
43016         
43017         dialCodeHolderEl : function()
43018         {
43019             var d = this.el.select('input.dial-code-holder',true).first();
43020             if(!d){
43021                 return false;
43022             }
43023             return d;
43024         },
43025         
43026         setDialCode : function(v)
43027         {
43028             this.dialCodeHolder.dom.value = '+'+v;
43029         },
43030         
43031         setFlagClass : function(n)
43032         {
43033             this.flag.dom.className = 'flag '+n;
43034         },
43035         
43036         getValue : function()
43037         {
43038             var v = this.inputEl().getValue();
43039             if(this.dialCodeHolder) {
43040                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43041             }
43042             return v;
43043         },
43044         
43045         setValue : function(v)
43046         {
43047             var d = this.getDialCode(v);
43048             
43049             //invalid dial code
43050             if(v.length == 0 || !d || d.length == 0) {
43051                 if(this.rendered){
43052                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43053                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43054                 }
43055                 return;
43056             }
43057             
43058             //valid dial code
43059             this.setFlagClass(this.dialCodeMapping[d].iso2);
43060             this.setDialCode(d);
43061             this.inputEl().dom.value = v.replace('+'+d,'');
43062             this.hiddenEl().dom.value = this.getValue();
43063             
43064             this.validate();
43065         },
43066         
43067         getDialCode : function(v)
43068         {
43069             v = v ||  '';
43070             
43071             if (v.length == 0) {
43072                 return this.dialCodeHolder.dom.value;
43073             }
43074             
43075             var dialCode = "";
43076             if (v.charAt(0) != "+") {
43077                 return false;
43078             }
43079             var numericChars = "";
43080             for (var i = 1; i < v.length; i++) {
43081               var c = v.charAt(i);
43082               if (!isNaN(c)) {
43083                 numericChars += c;
43084                 if (this.dialCodeMapping[numericChars]) {
43085                   dialCode = v.substr(1, i);
43086                 }
43087                 if (numericChars.length == 4) {
43088                   break;
43089                 }
43090               }
43091             }
43092             return dialCode;
43093         },
43094         
43095         reset : function()
43096         {
43097             this.setValue(this.defaultDialCode);
43098             this.validate();
43099         },
43100         
43101         hiddenEl : function()
43102         {
43103             return this.el.select('input.hidden-tel-input',true).first();
43104         },
43105         
43106         // after setting val
43107         onKeyUp : function(e){
43108             this.setValue(this.getValue());
43109         },
43110         
43111         onKeyPress : function(e){
43112             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43113                 e.stopEvent();
43114             }
43115         }
43116         
43117 });
43118 /**
43119  * @class Roo.bootstrap.MoneyField
43120  * @extends Roo.bootstrap.ComboBox
43121  * Bootstrap MoneyField class
43122  * 
43123  * @constructor
43124  * Create a new MoneyField.
43125  * @param {Object} config Configuration options
43126  */
43127
43128 Roo.bootstrap.MoneyField = function(config) {
43129     
43130     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43131     
43132 };
43133
43134 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43135     
43136     /**
43137      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43138      */
43139     allowDecimals : true,
43140     /**
43141      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43142      */
43143     decimalSeparator : ".",
43144     /**
43145      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43146      */
43147     decimalPrecision : 0,
43148     /**
43149      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43150      */
43151     allowNegative : true,
43152     /**
43153      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43154      */
43155     allowZero: true,
43156     /**
43157      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43158      */
43159     minValue : Number.NEGATIVE_INFINITY,
43160     /**
43161      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43162      */
43163     maxValue : Number.MAX_VALUE,
43164     /**
43165      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43166      */
43167     minText : "The minimum value for this field is {0}",
43168     /**
43169      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43170      */
43171     maxText : "The maximum value for this field is {0}",
43172     /**
43173      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43174      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43175      */
43176     nanText : "{0} is not a valid number",
43177     /**
43178      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43179      */
43180     castInt : true,
43181     /**
43182      * @cfg {String} defaults currency of the MoneyField
43183      * value should be in lkey
43184      */
43185     defaultCurrency : false,
43186     /**
43187      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43188      */
43189     thousandsDelimiter : false,
43190     /**
43191      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43192      */
43193     max_length: false,
43194     
43195     inputlg : 9,
43196     inputmd : 9,
43197     inputsm : 9,
43198     inputxs : 6,
43199     
43200     store : false,
43201     
43202     getAutoCreate : function()
43203     {
43204         var align = this.labelAlign || this.parentLabelAlign();
43205         
43206         var id = Roo.id();
43207
43208         var cfg = {
43209             cls: 'form-group',
43210             cn: []
43211         };
43212
43213         var input =  {
43214             tag: 'input',
43215             id : id,
43216             cls : 'form-control roo-money-amount-input',
43217             autocomplete: 'new-password'
43218         };
43219         
43220         var hiddenInput = {
43221             tag: 'input',
43222             type: 'hidden',
43223             id: Roo.id(),
43224             cls: 'hidden-number-input'
43225         };
43226         
43227         if(this.max_length) {
43228             input.maxlength = this.max_length; 
43229         }
43230         
43231         if (this.name) {
43232             hiddenInput.name = this.name;
43233         }
43234
43235         if (this.disabled) {
43236             input.disabled = true;
43237         }
43238
43239         var clg = 12 - this.inputlg;
43240         var cmd = 12 - this.inputmd;
43241         var csm = 12 - this.inputsm;
43242         var cxs = 12 - this.inputxs;
43243         
43244         var container = {
43245             tag : 'div',
43246             cls : 'row roo-money-field',
43247             cn : [
43248                 {
43249                     tag : 'div',
43250                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43251                     cn : [
43252                         {
43253                             tag : 'div',
43254                             cls: 'roo-select2-container input-group',
43255                             cn: [
43256                                 {
43257                                     tag : 'input',
43258                                     cls : 'form-control roo-money-currency-input',
43259                                     autocomplete: 'new-password',
43260                                     readOnly : 1,
43261                                     name : this.currencyName
43262                                 },
43263                                 {
43264                                     tag :'span',
43265                                     cls : 'input-group-addon',
43266                                     cn : [
43267                                         {
43268                                             tag: 'span',
43269                                             cls: 'caret'
43270                                         }
43271                                     ]
43272                                 }
43273                             ]
43274                         }
43275                     ]
43276                 },
43277                 {
43278                     tag : 'div',
43279                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43280                     cn : [
43281                         {
43282                             tag: 'div',
43283                             cls: this.hasFeedback ? 'has-feedback' : '',
43284                             cn: [
43285                                 input
43286                             ]
43287                         }
43288                     ]
43289                 }
43290             ]
43291             
43292         };
43293         
43294         if (this.fieldLabel.length) {
43295             var indicator = {
43296                 tag: 'i',
43297                 tooltip: 'This field is required'
43298             };
43299
43300             var label = {
43301                 tag: 'label',
43302                 'for':  id,
43303                 cls: 'control-label',
43304                 cn: []
43305             };
43306
43307             var label_text = {
43308                 tag: 'span',
43309                 html: this.fieldLabel
43310             };
43311
43312             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43313             label.cn = [
43314                 indicator,
43315                 label_text
43316             ];
43317
43318             if(this.indicatorpos == 'right') {
43319                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43320                 label.cn = [
43321                     label_text,
43322                     indicator
43323                 ];
43324             }
43325
43326             if(align == 'left') {
43327                 container = {
43328                     tag: 'div',
43329                     cn: [
43330                         container
43331                     ]
43332                 };
43333
43334                 if(this.labelWidth > 12){
43335                     label.style = "width: " + this.labelWidth + 'px';
43336                 }
43337                 if(this.labelWidth < 13 && this.labelmd == 0){
43338                     this.labelmd = this.labelWidth;
43339                 }
43340                 if(this.labellg > 0){
43341                     label.cls += ' col-lg-' + this.labellg;
43342                     input.cls += ' col-lg-' + (12 - this.labellg);
43343                 }
43344                 if(this.labelmd > 0){
43345                     label.cls += ' col-md-' + this.labelmd;
43346                     container.cls += ' col-md-' + (12 - this.labelmd);
43347                 }
43348                 if(this.labelsm > 0){
43349                     label.cls += ' col-sm-' + this.labelsm;
43350                     container.cls += ' col-sm-' + (12 - this.labelsm);
43351                 }
43352                 if(this.labelxs > 0){
43353                     label.cls += ' col-xs-' + this.labelxs;
43354                     container.cls += ' col-xs-' + (12 - this.labelxs);
43355                 }
43356             }
43357         }
43358
43359         cfg.cn = [
43360             label,
43361             container,
43362             hiddenInput
43363         ];
43364         
43365         var settings = this;
43366
43367         ['xs','sm','md','lg'].map(function(size){
43368             if (settings[size]) {
43369                 cfg.cls += ' col-' + size + '-' + settings[size];
43370             }
43371         });
43372         
43373         return cfg;
43374     },
43375     
43376     initEvents : function()
43377     {
43378         this.indicator = this.indicatorEl();
43379         
43380         this.initCurrencyEvent();
43381         
43382         this.initNumberEvent();
43383     },
43384     
43385     initCurrencyEvent : function()
43386     {
43387         if (!this.store) {
43388             throw "can not find store for combo";
43389         }
43390         
43391         this.store = Roo.factory(this.store, Roo.data);
43392         this.store.parent = this;
43393         
43394         this.createList();
43395         
43396         this.triggerEl = this.el.select('.input-group-addon', true).first();
43397         
43398         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43399         
43400         var _this = this;
43401         
43402         (function(){
43403             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43404             _this.list.setWidth(lw);
43405         }).defer(100);
43406         
43407         this.list.on('mouseover', this.onViewOver, this);
43408         this.list.on('mousemove', this.onViewMove, this);
43409         this.list.on('scroll', this.onViewScroll, this);
43410         
43411         if(!this.tpl){
43412             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43413         }
43414         
43415         this.view = new Roo.View(this.list, this.tpl, {
43416             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43417         });
43418         
43419         this.view.on('click', this.onViewClick, this);
43420         
43421         this.store.on('beforeload', this.onBeforeLoad, this);
43422         this.store.on('load', this.onLoad, this);
43423         this.store.on('loadexception', this.onLoadException, this);
43424         
43425         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43426             "up" : function(e){
43427                 this.inKeyMode = true;
43428                 this.selectPrev();
43429             },
43430
43431             "down" : function(e){
43432                 if(!this.isExpanded()){
43433                     this.onTriggerClick();
43434                 }else{
43435                     this.inKeyMode = true;
43436                     this.selectNext();
43437                 }
43438             },
43439
43440             "enter" : function(e){
43441                 this.collapse();
43442                 
43443                 if(this.fireEvent("specialkey", this, e)){
43444                     this.onViewClick(false);
43445                 }
43446                 
43447                 return true;
43448             },
43449
43450             "esc" : function(e){
43451                 this.collapse();
43452             },
43453
43454             "tab" : function(e){
43455                 this.collapse();
43456                 
43457                 if(this.fireEvent("specialkey", this, e)){
43458                     this.onViewClick(false);
43459                 }
43460                 
43461                 return true;
43462             },
43463
43464             scope : this,
43465
43466             doRelay : function(foo, bar, hname){
43467                 if(hname == 'down' || this.scope.isExpanded()){
43468                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43469                 }
43470                 return true;
43471             },
43472
43473             forceKeyDown: true
43474         });
43475         
43476         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43477         
43478     },
43479     
43480     initNumberEvent : function(e)
43481     {
43482         this.inputEl().on("keydown" , this.fireKey,  this);
43483         this.inputEl().on("focus", this.onFocus,  this);
43484         this.inputEl().on("blur", this.onBlur,  this);
43485         
43486         this.inputEl().relayEvent('keyup', this);
43487         
43488         if(this.indicator){
43489             this.indicator.addClass('invisible');
43490         }
43491  
43492         this.originalValue = this.getValue();
43493         
43494         if(this.validationEvent == 'keyup'){
43495             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43496             this.inputEl().on('keyup', this.filterValidation, this);
43497         }
43498         else if(this.validationEvent !== false){
43499             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43500         }
43501         
43502         if(this.selectOnFocus){
43503             this.on("focus", this.preFocus, this);
43504             
43505         }
43506         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43507             this.inputEl().on("keypress", this.filterKeys, this);
43508         } else {
43509             this.inputEl().relayEvent('keypress', this);
43510         }
43511         
43512         var allowed = "0123456789";
43513         
43514         if(this.allowDecimals){
43515             allowed += this.decimalSeparator;
43516         }
43517         
43518         if(this.allowNegative){
43519             allowed += "-";
43520         }
43521         
43522         if(this.thousandsDelimiter) {
43523             allowed += ",";
43524         }
43525         
43526         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43527         
43528         var keyPress = function(e){
43529             
43530             var k = e.getKey();
43531             
43532             var c = e.getCharCode();
43533             
43534             if(
43535                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43536                     allowed.indexOf(String.fromCharCode(c)) === -1
43537             ){
43538                 e.stopEvent();
43539                 return;
43540             }
43541             
43542             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43543                 return;
43544             }
43545             
43546             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43547                 e.stopEvent();
43548             }
43549         };
43550         
43551         this.inputEl().on("keypress", keyPress, this);
43552         
43553     },
43554     
43555     onTriggerClick : function(e)
43556     {   
43557         if(this.disabled){
43558             return;
43559         }
43560         
43561         this.page = 0;
43562         this.loadNext = false;
43563         
43564         if(this.isExpanded()){
43565             this.collapse();
43566             return;
43567         }
43568         
43569         this.hasFocus = true;
43570         
43571         if(this.triggerAction == 'all') {
43572             this.doQuery(this.allQuery, true);
43573             return;
43574         }
43575         
43576         this.doQuery(this.getRawValue());
43577     },
43578     
43579     getCurrency : function()
43580     {   
43581         var v = this.currencyEl().getValue();
43582         
43583         return v;
43584     },
43585     
43586     restrictHeight : function()
43587     {
43588         this.list.alignTo(this.currencyEl(), this.listAlign);
43589         this.list.alignTo(this.currencyEl(), this.listAlign);
43590     },
43591     
43592     onViewClick : function(view, doFocus, el, e)
43593     {
43594         var index = this.view.getSelectedIndexes()[0];
43595         
43596         var r = this.store.getAt(index);
43597         
43598         if(r){
43599             this.onSelect(r, index);
43600         }
43601     },
43602     
43603     onSelect : function(record, index){
43604         
43605         if(this.fireEvent('beforeselect', this, record, index) !== false){
43606         
43607             this.setFromCurrencyData(index > -1 ? record.data : false);
43608             
43609             this.collapse();
43610             
43611             this.fireEvent('select', this, record, index);
43612         }
43613     },
43614     
43615     setFromCurrencyData : function(o)
43616     {
43617         var currency = '';
43618         
43619         this.lastCurrency = o;
43620         
43621         if (this.currencyField) {
43622             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43623         } else {
43624             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43625         }
43626         
43627         this.lastSelectionText = currency;
43628         
43629         //setting default currency
43630         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43631             this.setCurrency(this.defaultCurrency);
43632             return;
43633         }
43634         
43635         this.setCurrency(currency);
43636     },
43637     
43638     setFromData : function(o)
43639     {
43640         var c = {};
43641         
43642         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43643         
43644         this.setFromCurrencyData(c);
43645         
43646         var value = '';
43647         
43648         if (this.name) {
43649             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43650         } else {
43651             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43652         }
43653         
43654         this.setValue(value);
43655         
43656     },
43657     
43658     setCurrency : function(v)
43659     {   
43660         this.currencyValue = v;
43661         
43662         if(this.rendered){
43663             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43664             this.validate();
43665         }
43666     },
43667     
43668     setValue : function(v)
43669     {
43670         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43671         
43672         this.value = v;
43673         
43674         if(this.rendered){
43675             
43676             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43677             
43678             this.inputEl().dom.value = (v == '') ? '' :
43679                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43680             
43681             if(!this.allowZero && v === '0') {
43682                 this.hiddenEl().dom.value = '';
43683                 this.inputEl().dom.value = '';
43684             }
43685             
43686             this.validate();
43687         }
43688     },
43689     
43690     getRawValue : function()
43691     {
43692         var v = this.inputEl().getValue();
43693         
43694         return v;
43695     },
43696     
43697     getValue : function()
43698     {
43699         return this.fixPrecision(this.parseValue(this.getRawValue()));
43700     },
43701     
43702     parseValue : function(value)
43703     {
43704         if(this.thousandsDelimiter) {
43705             value += "";
43706             r = new RegExp(",", "g");
43707             value = value.replace(r, "");
43708         }
43709         
43710         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43711         return isNaN(value) ? '' : value;
43712         
43713     },
43714     
43715     fixPrecision : function(value)
43716     {
43717         if(this.thousandsDelimiter) {
43718             value += "";
43719             r = new RegExp(",", "g");
43720             value = value.replace(r, "");
43721         }
43722         
43723         var nan = isNaN(value);
43724         
43725         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43726             return nan ? '' : value;
43727         }
43728         return parseFloat(value).toFixed(this.decimalPrecision);
43729     },
43730     
43731     decimalPrecisionFcn : function(v)
43732     {
43733         return Math.floor(v);
43734     },
43735     
43736     validateValue : function(value)
43737     {
43738         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43739             return false;
43740         }
43741         
43742         var num = this.parseValue(value);
43743         
43744         if(isNaN(num)){
43745             this.markInvalid(String.format(this.nanText, value));
43746             return false;
43747         }
43748         
43749         if(num < this.minValue){
43750             this.markInvalid(String.format(this.minText, this.minValue));
43751             return false;
43752         }
43753         
43754         if(num > this.maxValue){
43755             this.markInvalid(String.format(this.maxText, this.maxValue));
43756             return false;
43757         }
43758         
43759         return true;
43760     },
43761     
43762     validate : function()
43763     {
43764         if(this.disabled || this.allowBlank){
43765             this.markValid();
43766             return true;
43767         }
43768         
43769         var currency = this.getCurrency();
43770         
43771         if(this.validateValue(this.getRawValue()) && currency.length){
43772             this.markValid();
43773             return true;
43774         }
43775         
43776         this.markInvalid();
43777         return false;
43778     },
43779     
43780     getName: function()
43781     {
43782         return this.name;
43783     },
43784     
43785     beforeBlur : function()
43786     {
43787         if(!this.castInt){
43788             return;
43789         }
43790         
43791         var v = this.parseValue(this.getRawValue());
43792         
43793         if(v || v == 0){
43794             this.setValue(v);
43795         }
43796     },
43797     
43798     onBlur : function()
43799     {
43800         this.beforeBlur();
43801         
43802         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43803             //this.el.removeClass(this.focusClass);
43804         }
43805         
43806         this.hasFocus = false;
43807         
43808         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43809             this.validate();
43810         }
43811         
43812         var v = this.getValue();
43813         
43814         if(String(v) !== String(this.startValue)){
43815             this.fireEvent('change', this, v, this.startValue);
43816         }
43817         
43818         this.fireEvent("blur", this);
43819     },
43820     
43821     inputEl : function()
43822     {
43823         return this.el.select('.roo-money-amount-input', true).first();
43824     },
43825     
43826     currencyEl : function()
43827     {
43828         return this.el.select('.roo-money-currency-input', true).first();
43829     },
43830     
43831     hiddenEl : function()
43832     {
43833         return this.el.select('input.hidden-number-input',true).first();
43834     }
43835     
43836 });/**
43837  * @class Roo.bootstrap.BezierSignature
43838  * @extends Roo.bootstrap.Component
43839  * Bootstrap BezierSignature class
43840  * This script refer to:
43841  *    Title: Signature Pad
43842  *    Author: szimek
43843  *    Availability: https://github.com/szimek/signature_pad
43844  *
43845  * @constructor
43846  * Create a new BezierSignature
43847  * @param {Object} config The config object
43848  */
43849
43850 Roo.bootstrap.BezierSignature = function(config){
43851     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43852     this.addEvents({
43853         "resize" : true
43854     });
43855 };
43856
43857 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43858 {
43859      
43860     curve_data: [],
43861     
43862     is_empty: true,
43863     
43864     mouse_btn_down: true,
43865     
43866     /**
43867      * @cfg {int} canvas height
43868      */
43869     canvas_height: '200px',
43870     
43871     /**
43872      * @cfg {float|function} Radius of a single dot.
43873      */ 
43874     dot_size: false,
43875     
43876     /**
43877      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43878      */
43879     min_width: 0.5,
43880     
43881     /**
43882      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43883      */
43884     max_width: 2.5,
43885     
43886     /**
43887      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43888      */
43889     throttle: 16,
43890     
43891     /**
43892      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43893      */
43894     min_distance: 5,
43895     
43896     /**
43897      * @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.
43898      */
43899     bg_color: 'rgba(0, 0, 0, 0)',
43900     
43901     /**
43902      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43903      */
43904     dot_color: 'black',
43905     
43906     /**
43907      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43908      */ 
43909     velocity_filter_weight: 0.7,
43910     
43911     /**
43912      * @cfg {function} Callback when stroke begin. 
43913      */
43914     onBegin: false,
43915     
43916     /**
43917      * @cfg {function} Callback when stroke end.
43918      */
43919     onEnd: false,
43920     
43921     getAutoCreate : function()
43922     {
43923         var cls = 'roo-signature column';
43924         
43925         if(this.cls){
43926             cls += ' ' + this.cls;
43927         }
43928         
43929         var col_sizes = [
43930             'lg',
43931             'md',
43932             'sm',
43933             'xs'
43934         ];
43935         
43936         for(var i = 0; i < col_sizes.length; i++) {
43937             if(this[col_sizes[i]]) {
43938                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43939             }
43940         }
43941         
43942         var cfg = {
43943             tag: 'div',
43944             cls: cls,
43945             cn: [
43946                 {
43947                     tag: 'div',
43948                     cls: 'roo-signature-body',
43949                     cn: [
43950                         {
43951                             tag: 'canvas',
43952                             cls: 'roo-signature-body-canvas',
43953                             height: this.canvas_height,
43954                             width: this.canvas_width
43955                         }
43956                     ]
43957                 },
43958                 {
43959                     tag: 'input',
43960                     type: 'file',
43961                     style: 'display: none'
43962                 }
43963             ]
43964         };
43965         
43966         return cfg;
43967     },
43968     
43969     initEvents: function() 
43970     {
43971         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43972         
43973         var canvas = this.canvasEl();
43974         
43975         // mouse && touch event swapping...
43976         canvas.dom.style.touchAction = 'none';
43977         canvas.dom.style.msTouchAction = 'none';
43978         
43979         this.mouse_btn_down = false;
43980         canvas.on('mousedown', this._handleMouseDown, this);
43981         canvas.on('mousemove', this._handleMouseMove, this);
43982         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43983         
43984         if (window.PointerEvent) {
43985             canvas.on('pointerdown', this._handleMouseDown, this);
43986             canvas.on('pointermove', this._handleMouseMove, this);
43987             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43988         }
43989         
43990         if ('ontouchstart' in window) {
43991             canvas.on('touchstart', this._handleTouchStart, this);
43992             canvas.on('touchmove', this._handleTouchMove, this);
43993             canvas.on('touchend', this._handleTouchEnd, this);
43994         }
43995         
43996         Roo.EventManager.onWindowResize(this.resize, this, true);
43997         
43998         // file input event
43999         this.fileEl().on('change', this.uploadImage, this);
44000         
44001         this.clear();
44002         
44003         this.resize();
44004     },
44005     
44006     resize: function(){
44007         
44008         var canvas = this.canvasEl().dom;
44009         var ctx = this.canvasElCtx();
44010         var img_data = false;
44011         
44012         if(canvas.width > 0) {
44013             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44014         }
44015         // setting canvas width will clean img data
44016         canvas.width = 0;
44017         
44018         var style = window.getComputedStyle ? 
44019             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44020             
44021         var padding_left = parseInt(style.paddingLeft) || 0;
44022         var padding_right = parseInt(style.paddingRight) || 0;
44023         
44024         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44025         
44026         if(img_data) {
44027             ctx.putImageData(img_data, 0, 0);
44028         }
44029     },
44030     
44031     _handleMouseDown: function(e)
44032     {
44033         if (e.browserEvent.which === 1) {
44034             this.mouse_btn_down = true;
44035             this.strokeBegin(e);
44036         }
44037     },
44038     
44039     _handleMouseMove: function (e)
44040     {
44041         if (this.mouse_btn_down) {
44042             this.strokeMoveUpdate(e);
44043         }
44044     },
44045     
44046     _handleMouseUp: function (e)
44047     {
44048         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44049             this.mouse_btn_down = false;
44050             this.strokeEnd(e);
44051         }
44052     },
44053     
44054     _handleTouchStart: function (e) {
44055         
44056         e.preventDefault();
44057         if (e.browserEvent.targetTouches.length === 1) {
44058             // var touch = e.browserEvent.changedTouches[0];
44059             // this.strokeBegin(touch);
44060             
44061              this.strokeBegin(e); // assume e catching the correct xy...
44062         }
44063     },
44064     
44065     _handleTouchMove: function (e) {
44066         e.preventDefault();
44067         // var touch = event.targetTouches[0];
44068         // _this._strokeMoveUpdate(touch);
44069         this.strokeMoveUpdate(e);
44070     },
44071     
44072     _handleTouchEnd: function (e) {
44073         var wasCanvasTouched = e.target === this.canvasEl().dom;
44074         if (wasCanvasTouched) {
44075             e.preventDefault();
44076             // var touch = event.changedTouches[0];
44077             // _this._strokeEnd(touch);
44078             this.strokeEnd(e);
44079         }
44080     },
44081     
44082     reset: function () {
44083         this._lastPoints = [];
44084         this._lastVelocity = 0;
44085         this._lastWidth = (this.min_width + this.max_width) / 2;
44086         this.canvasElCtx().fillStyle = this.dot_color;
44087     },
44088     
44089     strokeMoveUpdate: function(e)
44090     {
44091         this.strokeUpdate(e);
44092         
44093         if (this.throttle) {
44094             this.throttleStroke(this.strokeUpdate, this.throttle);
44095         }
44096         else {
44097             this.strokeUpdate(e);
44098         }
44099     },
44100     
44101     strokeBegin: function(e)
44102     {
44103         var newPointGroup = {
44104             color: this.dot_color,
44105             points: []
44106         };
44107         
44108         if (typeof this.onBegin === 'function') {
44109             this.onBegin(e);
44110         }
44111         
44112         this.curve_data.push(newPointGroup);
44113         this.reset();
44114         this.strokeUpdate(e);
44115     },
44116     
44117     strokeUpdate: function(e)
44118     {
44119         var rect = this.canvasEl().dom.getBoundingClientRect();
44120         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44121         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44122         var lastPoints = lastPointGroup.points;
44123         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44124         var isLastPointTooClose = lastPoint
44125             ? point.distanceTo(lastPoint) <= this.min_distance
44126             : false;
44127         var color = lastPointGroup.color;
44128         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44129             var curve = this.addPoint(point);
44130             if (!lastPoint) {
44131                 this.drawDot({color: color, point: point});
44132             }
44133             else if (curve) {
44134                 this.drawCurve({color: color, curve: curve});
44135             }
44136             lastPoints.push({
44137                 time: point.time,
44138                 x: point.x,
44139                 y: point.y
44140             });
44141         }
44142     },
44143     
44144     strokeEnd: function(e)
44145     {
44146         this.strokeUpdate(e);
44147         if (typeof this.onEnd === 'function') {
44148             this.onEnd(e);
44149         }
44150     },
44151     
44152     addPoint:  function (point) {
44153         var _lastPoints = this._lastPoints;
44154         _lastPoints.push(point);
44155         if (_lastPoints.length > 2) {
44156             if (_lastPoints.length === 3) {
44157                 _lastPoints.unshift(_lastPoints[0]);
44158             }
44159             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44160             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44161             _lastPoints.shift();
44162             return curve;
44163         }
44164         return null;
44165     },
44166     
44167     calculateCurveWidths: function (startPoint, endPoint) {
44168         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44169             (1 - this.velocity_filter_weight) * this._lastVelocity;
44170
44171         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44172         var widths = {
44173             end: newWidth,
44174             start: this._lastWidth
44175         };
44176         
44177         this._lastVelocity = velocity;
44178         this._lastWidth = newWidth;
44179         return widths;
44180     },
44181     
44182     drawDot: function (_a) {
44183         var color = _a.color, point = _a.point;
44184         var ctx = this.canvasElCtx();
44185         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44186         ctx.beginPath();
44187         this.drawCurveSegment(point.x, point.y, width);
44188         ctx.closePath();
44189         ctx.fillStyle = color;
44190         ctx.fill();
44191     },
44192     
44193     drawCurve: function (_a) {
44194         var color = _a.color, curve = _a.curve;
44195         var ctx = this.canvasElCtx();
44196         var widthDelta = curve.endWidth - curve.startWidth;
44197         var drawSteps = Math.floor(curve.length()) * 2;
44198         ctx.beginPath();
44199         ctx.fillStyle = color;
44200         for (var i = 0; i < drawSteps; i += 1) {
44201         var t = i / drawSteps;
44202         var tt = t * t;
44203         var ttt = tt * t;
44204         var u = 1 - t;
44205         var uu = u * u;
44206         var uuu = uu * u;
44207         var x = uuu * curve.startPoint.x;
44208         x += 3 * uu * t * curve.control1.x;
44209         x += 3 * u * tt * curve.control2.x;
44210         x += ttt * curve.endPoint.x;
44211         var y = uuu * curve.startPoint.y;
44212         y += 3 * uu * t * curve.control1.y;
44213         y += 3 * u * tt * curve.control2.y;
44214         y += ttt * curve.endPoint.y;
44215         var width = curve.startWidth + ttt * widthDelta;
44216         this.drawCurveSegment(x, y, width);
44217         }
44218         ctx.closePath();
44219         ctx.fill();
44220     },
44221     
44222     drawCurveSegment: function (x, y, width) {
44223         var ctx = this.canvasElCtx();
44224         ctx.moveTo(x, y);
44225         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44226         this.is_empty = false;
44227     },
44228     
44229     clear: function()
44230     {
44231         var ctx = this.canvasElCtx();
44232         var canvas = this.canvasEl().dom;
44233         ctx.fillStyle = this.bg_color;
44234         ctx.clearRect(0, 0, canvas.width, canvas.height);
44235         ctx.fillRect(0, 0, canvas.width, canvas.height);
44236         this.curve_data = [];
44237         this.reset();
44238         this.is_empty = true;
44239     },
44240     
44241     fileEl: function()
44242     {
44243         return  this.el.select('input',true).first();
44244     },
44245     
44246     canvasEl: function()
44247     {
44248         return this.el.select('canvas',true).first();
44249     },
44250     
44251     canvasElCtx: function()
44252     {
44253         return this.el.select('canvas',true).first().dom.getContext('2d');
44254     },
44255     
44256     getImage: function(type)
44257     {
44258         if(this.is_empty) {
44259             return false;
44260         }
44261         
44262         // encryption ?
44263         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44264     },
44265     
44266     drawFromImage: function(img_src)
44267     {
44268         var img = new Image();
44269         
44270         img.onload = function(){
44271             this.canvasElCtx().drawImage(img, 0, 0);
44272         }.bind(this);
44273         
44274         img.src = img_src;
44275         
44276         this.is_empty = false;
44277     },
44278     
44279     selectImage: function()
44280     {
44281         this.fileEl().dom.click();
44282     },
44283     
44284     uploadImage: function(e)
44285     {
44286         var reader = new FileReader();
44287         
44288         reader.onload = function(e){
44289             var img = new Image();
44290             img.onload = function(){
44291                 this.reset();
44292                 this.canvasElCtx().drawImage(img, 0, 0);
44293             }.bind(this);
44294             img.src = e.target.result;
44295         }.bind(this);
44296         
44297         reader.readAsDataURL(e.target.files[0]);
44298     },
44299     
44300     // Bezier Point Constructor
44301     Point: (function () {
44302         function Point(x, y, time) {
44303             this.x = x;
44304             this.y = y;
44305             this.time = time || Date.now();
44306         }
44307         Point.prototype.distanceTo = function (start) {
44308             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44309         };
44310         Point.prototype.equals = function (other) {
44311             return this.x === other.x && this.y === other.y && this.time === other.time;
44312         };
44313         Point.prototype.velocityFrom = function (start) {
44314             return this.time !== start.time
44315             ? this.distanceTo(start) / (this.time - start.time)
44316             : 0;
44317         };
44318         return Point;
44319     }()),
44320     
44321     
44322     // Bezier Constructor
44323     Bezier: (function () {
44324         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44325             this.startPoint = startPoint;
44326             this.control2 = control2;
44327             this.control1 = control1;
44328             this.endPoint = endPoint;
44329             this.startWidth = startWidth;
44330             this.endWidth = endWidth;
44331         }
44332         Bezier.fromPoints = function (points, widths, scope) {
44333             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44334             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44335             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44336         };
44337         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44338             var dx1 = s1.x - s2.x;
44339             var dy1 = s1.y - s2.y;
44340             var dx2 = s2.x - s3.x;
44341             var dy2 = s2.y - s3.y;
44342             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44343             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44344             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44345             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44346             var dxm = m1.x - m2.x;
44347             var dym = m1.y - m2.y;
44348             var k = l2 / (l1 + l2);
44349             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44350             var tx = s2.x - cm.x;
44351             var ty = s2.y - cm.y;
44352             return {
44353                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44354                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44355             };
44356         };
44357         Bezier.prototype.length = function () {
44358             var steps = 10;
44359             var length = 0;
44360             var px;
44361             var py;
44362             for (var i = 0; i <= steps; i += 1) {
44363                 var t = i / steps;
44364                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44365                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44366                 if (i > 0) {
44367                     var xdiff = cx - px;
44368                     var ydiff = cy - py;
44369                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44370                 }
44371                 px = cx;
44372                 py = cy;
44373             }
44374             return length;
44375         };
44376         Bezier.prototype.point = function (t, start, c1, c2, end) {
44377             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44378             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44379             + (3.0 * c2 * (1.0 - t) * t * t)
44380             + (end * t * t * t);
44381         };
44382         return Bezier;
44383     }()),
44384     
44385     throttleStroke: function(fn, wait) {
44386       if (wait === void 0) { wait = 250; }
44387       var previous = 0;
44388       var timeout = null;
44389       var result;
44390       var storedContext;
44391       var storedArgs;
44392       var later = function () {
44393           previous = Date.now();
44394           timeout = null;
44395           result = fn.apply(storedContext, storedArgs);
44396           if (!timeout) {
44397               storedContext = null;
44398               storedArgs = [];
44399           }
44400       };
44401       return function wrapper() {
44402           var args = [];
44403           for (var _i = 0; _i < arguments.length; _i++) {
44404               args[_i] = arguments[_i];
44405           }
44406           var now = Date.now();
44407           var remaining = wait - (now - previous);
44408           storedContext = this;
44409           storedArgs = args;
44410           if (remaining <= 0 || remaining > wait) {
44411               if (timeout) {
44412                   clearTimeout(timeout);
44413                   timeout = null;
44414               }
44415               previous = now;
44416               result = fn.apply(storedContext, storedArgs);
44417               if (!timeout) {
44418                   storedContext = null;
44419                   storedArgs = [];
44420               }
44421           }
44422           else if (!timeout) {
44423               timeout = window.setTimeout(later, remaining);
44424           }
44425           return result;
44426       };
44427   }
44428   
44429 });
44430
44431  
44432
44433