Roo/bootstrap/Popover.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3951  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3952  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3953  * @cfg {String} size (sm|lg|xl) default empty
3954  * @cfg {Number} max_width set the max width of modal
3955  * @cfg {Boolean} editableTitle can the title be edited
3956
3957  *
3958  *
3959  * @constructor
3960  * Create a new Modal Dialog
3961  * @param {Object} config The config object
3962  */
3963
3964 Roo.bootstrap.Modal = function(config){
3965     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3966     this.addEvents({
3967         // raw events
3968         /**
3969          * @event btnclick
3970          * The raw btnclick event for the button
3971          * @param {Roo.EventObject} e
3972          */
3973         "btnclick" : true,
3974         /**
3975          * @event resize
3976          * Fire when dialog resize
3977          * @param {Roo.bootstrap.Modal} this
3978          * @param {Roo.EventObject} e
3979          */
3980         "resize" : true,
3981         /**
3982          * @event titlechanged
3983          * Fire when the editable title has been changed
3984          * @param {Roo.bootstrap.Modal} this
3985          * @param {Roo.EventObject} value
3986          */
3987         "titlechanged" : true 
3988         
3989     });
3990     this.buttons = this.buttons || [];
3991
3992     if (this.tmpl) {
3993         this.tmpl = Roo.factory(this.tmpl);
3994     }
3995
3996 };
3997
3998 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3999
4000     title : 'test dialog',
4001
4002     buttons : false,
4003
4004     // set on load...
4005
4006     html: false,
4007
4008     tmp: false,
4009
4010     specificTitle: false,
4011
4012     buttonPosition: 'right',
4013
4014     allow_close : true,
4015
4016     animate : true,
4017
4018     fitwindow: false,
4019     
4020      // private
4021     dialogEl: false,
4022     bodyEl:  false,
4023     footerEl:  false,
4024     titleEl:  false,
4025     closeEl:  false,
4026
4027     size: '',
4028     
4029     max_width: 0,
4030     
4031     max_height: 0,
4032     
4033     fit_content: false,
4034     editableTitle  : false,
4035
4036     onRender : function(ct, position)
4037     {
4038         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4039
4040         if(!this.el){
4041             var cfg = Roo.apply({},  this.getAutoCreate());
4042             cfg.id = Roo.id();
4043             //if(!cfg.name){
4044             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4045             //}
4046             //if (!cfg.name.length) {
4047             //    delete cfg.name;
4048            // }
4049             if (this.cls) {
4050                 cfg.cls += ' ' + this.cls;
4051             }
4052             if (this.style) {
4053                 cfg.style = this.style;
4054             }
4055             this.el = Roo.get(document.body).createChild(cfg, position);
4056         }
4057         //var type = this.el.dom.type;
4058
4059
4060         if(this.tabIndex !== undefined){
4061             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4062         }
4063
4064         this.dialogEl = this.el.select('.modal-dialog',true).first();
4065         this.bodyEl = this.el.select('.modal-body',true).first();
4066         this.closeEl = this.el.select('.modal-header .close', true).first();
4067         this.headerEl = this.el.select('.modal-header',true).first();
4068         this.titleEl = this.el.select('.modal-title',true).first();
4069         this.footerEl = this.el.select('.modal-footer',true).first();
4070
4071         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4072         
4073         //this.el.addClass("x-dlg-modal");
4074
4075         if (this.buttons.length) {
4076             Roo.each(this.buttons, function(bb) {
4077                 var b = Roo.apply({}, bb);
4078                 b.xns = b.xns || Roo.bootstrap;
4079                 b.xtype = b.xtype || 'Button';
4080                 if (typeof(b.listeners) == 'undefined') {
4081                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4082                 }
4083
4084                 var btn = Roo.factory(b);
4085
4086                 btn.render(this.getButtonContainer());
4087
4088             },this);
4089         }
4090         // render the children.
4091         var nitems = [];
4092
4093         if(typeof(this.items) != 'undefined'){
4094             var items = this.items;
4095             delete this.items;
4096
4097             for(var i =0;i < items.length;i++) {
4098                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4099             }
4100         }
4101
4102         this.items = nitems;
4103
4104         // where are these used - they used to be body/close/footer
4105
4106
4107         this.initEvents();
4108         //this.el.addClass([this.fieldClass, this.cls]);
4109
4110     },
4111
4112     getAutoCreate : function()
4113     {
4114         // we will default to modal-body-overflow - might need to remove or make optional later.
4115         var bdy = {
4116                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4117                 html : this.html || ''
4118         };
4119
4120         var title = {
4121             tag: 'h5',
4122             cls : 'modal-title',
4123             html : this.title
4124         };
4125
4126         if(this.specificTitle){ // WTF is this?
4127             title = this.title;
4128         }
4129
4130         var header = [];
4131         if (this.allow_close && Roo.bootstrap.version == 3) {
4132             header.push({
4133                 tag: 'button',
4134                 cls : 'close',
4135                 html : '&times'
4136             });
4137         }
4138
4139         header.push(title);
4140
4141         if (this.editableTitle) {
4142             header.push({
4143                 cls: 'form-control roo-editable-title d-none',
4144                 tag: 'input',
4145                 type: 'text'
4146             });
4147         }
4148         
4149         if (this.allow_close && Roo.bootstrap.version == 4) {
4150             header.push({
4151                 tag: 'button',
4152                 cls : 'close',
4153                 html : '&times'
4154             });
4155         }
4156         
4157         var size = '';
4158
4159         if(this.size.length){
4160             size = 'modal-' + this.size;
4161         }
4162         
4163         var footer = Roo.bootstrap.version == 3 ?
4164             {
4165                 cls : 'modal-footer',
4166                 cn : [
4167                     {
4168                         tag: 'div',
4169                         cls: 'btn-' + this.buttonPosition
4170                     }
4171                 ]
4172
4173             } :
4174             {  // BS4 uses mr-auto on left buttons....
4175                 cls : 'modal-footer'
4176             };
4177
4178             
4179
4180         
4181         
4182         var modal = {
4183             cls: "modal",
4184              cn : [
4185                 {
4186                     cls: "modal-dialog " + size,
4187                     cn : [
4188                         {
4189                             cls : "modal-content",
4190                             cn : [
4191                                 {
4192                                     cls : 'modal-header',
4193                                     cn : header
4194                                 },
4195                                 bdy,
4196                                 footer
4197                             ]
4198
4199                         }
4200                     ]
4201
4202                 }
4203             ]
4204         };
4205
4206         if(this.animate){
4207             modal.cls += ' fade';
4208         }
4209
4210         return modal;
4211
4212     },
4213     getChildContainer : function() {
4214
4215          return this.bodyEl;
4216
4217     },
4218     getButtonContainer : function() {
4219         
4220          return Roo.bootstrap.version == 4 ?
4221             this.el.select('.modal-footer',true).first()
4222             : this.el.select('.modal-footer div',true).first();
4223
4224     },
4225     initEvents : function()
4226     {
4227         if (this.allow_close) {
4228             this.closeEl.on('click', this.hide, this);
4229         }
4230         Roo.EventManager.onWindowResize(this.resize, this, true);
4231         if (this.editableTitle) {
4232             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4233             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4234             this.headerEditEl.on('keyup', function(e) {
4235                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4236                         this.toggleHeaderInput(false)
4237                     }
4238                 }, this);
4239             this.headerEditEl.on('blur', function(e) {
4240                 this.toggleHeaderInput(false)
4241             },this);
4242         }
4243
4244     },
4245   
4246
4247     resize : function()
4248     {
4249         this.maskEl.setSize(
4250             Roo.lib.Dom.getViewWidth(true),
4251             Roo.lib.Dom.getViewHeight(true)
4252         );
4253         
4254         if (this.fitwindow) {
4255             
4256            this.dialogEl.setStyle( { 'max-width' : '100%' });
4257             this.setSize(
4258                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4259                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4260             );
4261             return;
4262         }
4263         
4264         if(this.max_width !== 0) {
4265             
4266             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4267             
4268             if(this.height) {
4269                 this.setSize(w, this.height);
4270                 return;
4271             }
4272             
4273             if(this.max_height) {
4274                 this.setSize(w,Math.min(
4275                     this.max_height,
4276                     Roo.lib.Dom.getViewportHeight(true) - 60
4277                 ));
4278                 
4279                 return;
4280             }
4281             
4282             if(!this.fit_content) {
4283                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4284                 return;
4285             }
4286             
4287             this.setSize(w, Math.min(
4288                 60 +
4289                 this.headerEl.getHeight() + 
4290                 this.footerEl.getHeight() + 
4291                 this.getChildHeight(this.bodyEl.dom.childNodes),
4292                 Roo.lib.Dom.getViewportHeight(true) - 60)
4293             );
4294         }
4295         
4296     },
4297
4298     setSize : function(w,h)
4299     {
4300         if (!w && !h) {
4301             return;
4302         }
4303         
4304         this.resizeTo(w,h);
4305     },
4306
4307     show : function() {
4308
4309         if (!this.rendered) {
4310             this.render();
4311         }
4312         this.toggleHeaderInput(false);
4313         //this.el.setStyle('display', 'block');
4314         this.el.removeClass('hideing');
4315         this.el.dom.style.display='block';
4316         
4317         Roo.get(document.body).addClass('modal-open');
4318  
4319         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4320             
4321             (function(){
4322                 this.el.addClass('show');
4323                 this.el.addClass('in');
4324             }).defer(50, this);
4325         }else{
4326             this.el.addClass('show');
4327             this.el.addClass('in');
4328         }
4329
4330         // not sure how we can show data in here..
4331         //if (this.tmpl) {
4332         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4333         //}
4334
4335         Roo.get(document.body).addClass("x-body-masked");
4336         
4337         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4338         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4339         this.maskEl.dom.style.display = 'block';
4340         this.maskEl.addClass('show');
4341         
4342         
4343         this.resize();
4344         
4345         this.fireEvent('show', this);
4346
4347         // set zindex here - otherwise it appears to be ignored...
4348         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4349
4350         (function () {
4351             this.items.forEach( function(e) {
4352                 e.layout ? e.layout() : false;
4353
4354             });
4355         }).defer(100,this);
4356
4357     },
4358     hide : function()
4359     {
4360         if(this.fireEvent("beforehide", this) !== false){
4361             
4362             this.maskEl.removeClass('show');
4363             
4364             this.maskEl.dom.style.display = '';
4365             Roo.get(document.body).removeClass("x-body-masked");
4366             this.el.removeClass('in');
4367             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4368
4369             if(this.animate){ // why
4370                 this.el.addClass('hideing');
4371                 this.el.removeClass('show');
4372                 (function(){
4373                     if (!this.el.hasClass('hideing')) {
4374                         return; // it's been shown again...
4375                     }
4376                     
4377                     this.el.dom.style.display='';
4378
4379                     Roo.get(document.body).removeClass('modal-open');
4380                     this.el.removeClass('hideing');
4381                 }).defer(150,this);
4382                 
4383             }else{
4384                 this.el.removeClass('show');
4385                 this.el.dom.style.display='';
4386                 Roo.get(document.body).removeClass('modal-open');
4387
4388             }
4389             this.fireEvent('hide', this);
4390         }
4391     },
4392     isVisible : function()
4393     {
4394         
4395         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4396         
4397     },
4398
4399     addButton : function(str, cb)
4400     {
4401
4402
4403         var b = Roo.apply({}, { html : str } );
4404         b.xns = b.xns || Roo.bootstrap;
4405         b.xtype = b.xtype || 'Button';
4406         if (typeof(b.listeners) == 'undefined') {
4407             b.listeners = { click : cb.createDelegate(this)  };
4408         }
4409
4410         var btn = Roo.factory(b);
4411
4412         btn.render(this.getButtonContainer());
4413
4414         return btn;
4415
4416     },
4417
4418     setDefaultButton : function(btn)
4419     {
4420         //this.el.select('.modal-footer').()
4421     },
4422
4423     resizeTo: function(w,h)
4424     {
4425         this.dialogEl.setWidth(w);
4426         
4427         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4428
4429         this.bodyEl.setHeight(h - diff);
4430         
4431         this.fireEvent('resize', this);
4432     },
4433     
4434     setContentSize  : function(w, h)
4435     {
4436
4437     },
4438     onButtonClick: function(btn,e)
4439     {
4440         //Roo.log([a,b,c]);
4441         this.fireEvent('btnclick', btn.name, e);
4442     },
4443      /**
4444      * Set the title of the Dialog
4445      * @param {String} str new Title
4446      */
4447     setTitle: function(str) {
4448         this.titleEl.dom.innerHTML = str;
4449         this.title = str;
4450     },
4451     /**
4452      * Set the body of the Dialog
4453      * @param {String} str new Title
4454      */
4455     setBody: function(str) {
4456         this.bodyEl.dom.innerHTML = str;
4457     },
4458     /**
4459      * Set the body of the Dialog using the template
4460      * @param {Obj} data - apply this data to the template and replace the body contents.
4461      */
4462     applyBody: function(obj)
4463     {
4464         if (!this.tmpl) {
4465             Roo.log("Error - using apply Body without a template");
4466             //code
4467         }
4468         this.tmpl.overwrite(this.bodyEl, obj);
4469     },
4470     
4471     getChildHeight : function(child_nodes)
4472     {
4473         if(
4474             !child_nodes ||
4475             child_nodes.length == 0
4476         ) {
4477             return 0;
4478         }
4479         
4480         var child_height = 0;
4481         
4482         for(var i = 0; i < child_nodes.length; i++) {
4483             
4484             /*
4485             * for modal with tabs...
4486             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4487                 
4488                 var layout_childs = child_nodes[i].childNodes;
4489                 
4490                 for(var j = 0; j < layout_childs.length; j++) {
4491                     
4492                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4493                         
4494                         var layout_body_childs = layout_childs[j].childNodes;
4495                         
4496                         for(var k = 0; k < layout_body_childs.length; k++) {
4497                             
4498                             if(layout_body_childs[k].classList.contains('navbar')) {
4499                                 child_height += layout_body_childs[k].offsetHeight;
4500                                 continue;
4501                             }
4502                             
4503                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4504                                 
4505                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4506                                 
4507                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4508                                     
4509                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4510                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4511                                         continue;
4512                                     }
4513                                     
4514                                 }
4515                                 
4516                             }
4517                             
4518                         }
4519                     }
4520                 }
4521                 continue;
4522             }
4523             */
4524             
4525             child_height += child_nodes[i].offsetHeight;
4526             // Roo.log(child_nodes[i].offsetHeight);
4527         }
4528         
4529         return child_height;
4530     },
4531     toggleHeaderInput : function(is_edit)
4532     {
4533         if (!this.editableTitle) {
4534             return; // not editable.
4535         }
4536         if (is_edit && this.is_header_editing) {
4537             return; // already editing..
4538         }
4539         if (is_edit) {
4540     
4541             this.headerEditEl.dom.value = this.title;
4542             this.headerEditEl.removeClass('d-none');
4543             this.headerEditEl.dom.focus();
4544             this.titleEl.addClass('d-none');
4545             
4546             this.is_header_editing = true;
4547             return
4548         }
4549         // flip back to not editing.
4550         this.title = this.headerEditEl.dom.value;
4551         this.headerEditEl.addClass('d-none');
4552         this.titleEl.removeClass('d-none');
4553         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4554         this.is_header_editing = false;
4555         this.fireEvent('titlechanged', this, this.title);
4556     
4557             
4558         
4559     }
4560
4561 });
4562
4563
4564 Roo.apply(Roo.bootstrap.Modal,  {
4565     /**
4566          * Button config that displays a single OK button
4567          * @type Object
4568          */
4569         OK :  [{
4570             name : 'ok',
4571             weight : 'primary',
4572             html : 'OK'
4573         }],
4574         /**
4575          * Button config that displays Yes and No buttons
4576          * @type Object
4577          */
4578         YESNO : [
4579             {
4580                 name  : 'no',
4581                 html : 'No'
4582             },
4583             {
4584                 name  :'yes',
4585                 weight : 'primary',
4586                 html : 'Yes'
4587             }
4588         ],
4589
4590         /**
4591          * Button config that displays OK and Cancel buttons
4592          * @type Object
4593          */
4594         OKCANCEL : [
4595             {
4596                name : 'cancel',
4597                 html : 'Cancel'
4598             },
4599             {
4600                 name : 'ok',
4601                 weight : 'primary',
4602                 html : 'OK'
4603             }
4604         ],
4605         /**
4606          * Button config that displays Yes, No and Cancel buttons
4607          * @type Object
4608          */
4609         YESNOCANCEL : [
4610             {
4611                 name : 'yes',
4612                 weight : 'primary',
4613                 html : 'Yes'
4614             },
4615             {
4616                 name : 'no',
4617                 html : 'No'
4618             },
4619             {
4620                 name : 'cancel',
4621                 html : 'Cancel'
4622             }
4623         ],
4624         
4625         zIndex : 10001
4626 });
4627
4628 /*
4629  * - LGPL
4630  *
4631  * messagebox - can be used as a replace
4632  * 
4633  */
4634 /**
4635  * @class Roo.MessageBox
4636  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4637  * Example usage:
4638  *<pre><code>
4639 // Basic alert:
4640 Roo.Msg.alert('Status', 'Changes saved successfully.');
4641
4642 // Prompt for user data:
4643 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4644     if (btn == 'ok'){
4645         // process text value...
4646     }
4647 });
4648
4649 // Show a dialog using config options:
4650 Roo.Msg.show({
4651    title:'Save Changes?',
4652    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4653    buttons: Roo.Msg.YESNOCANCEL,
4654    fn: processResult,
4655    animEl: 'elId'
4656 });
4657 </code></pre>
4658  * @singleton
4659  */
4660 Roo.bootstrap.MessageBox = function(){
4661     var dlg, opt, mask, waitTimer;
4662     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4663     var buttons, activeTextEl, bwidth;
4664
4665     
4666     // private
4667     var handleButton = function(button){
4668         dlg.hide();
4669         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4670     };
4671
4672     // private
4673     var handleHide = function(){
4674         if(opt && opt.cls){
4675             dlg.el.removeClass(opt.cls);
4676         }
4677         //if(waitTimer){
4678         //    Roo.TaskMgr.stop(waitTimer);
4679         //    waitTimer = null;
4680         //}
4681     };
4682
4683     // private
4684     var updateButtons = function(b){
4685         var width = 0;
4686         if(!b){
4687             buttons["ok"].hide();
4688             buttons["cancel"].hide();
4689             buttons["yes"].hide();
4690             buttons["no"].hide();
4691             dlg.footerEl.hide();
4692             
4693             return width;
4694         }
4695         dlg.footerEl.show();
4696         for(var k in buttons){
4697             if(typeof buttons[k] != "function"){
4698                 if(b[k]){
4699                     buttons[k].show();
4700                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4701                     width += buttons[k].el.getWidth()+15;
4702                 }else{
4703                     buttons[k].hide();
4704                 }
4705             }
4706         }
4707         return width;
4708     };
4709
4710     // private
4711     var handleEsc = function(d, k, e){
4712         if(opt && opt.closable !== false){
4713             dlg.hide();
4714         }
4715         if(e){
4716             e.stopEvent();
4717         }
4718     };
4719
4720     return {
4721         /**
4722          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4723          * @return {Roo.BasicDialog} The BasicDialog element
4724          */
4725         getDialog : function(){
4726            if(!dlg){
4727                 dlg = new Roo.bootstrap.Modal( {
4728                     //draggable: true,
4729                     //resizable:false,
4730                     //constraintoviewport:false,
4731                     //fixedcenter:true,
4732                     //collapsible : false,
4733                     //shim:true,
4734                     //modal: true,
4735                 //    width: 'auto',
4736                   //  height:100,
4737                     //buttonAlign:"center",
4738                     closeClick : function(){
4739                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4740                             handleButton("no");
4741                         }else{
4742                             handleButton("cancel");
4743                         }
4744                     }
4745                 });
4746                 dlg.render();
4747                 dlg.on("hide", handleHide);
4748                 mask = dlg.mask;
4749                 //dlg.addKeyListener(27, handleEsc);
4750                 buttons = {};
4751                 this.buttons = buttons;
4752                 var bt = this.buttonText;
4753                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4754                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4755                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4756                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4757                 //Roo.log(buttons);
4758                 bodyEl = dlg.bodyEl.createChild({
4759
4760                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4761                         '<textarea class="roo-mb-textarea"></textarea>' +
4762                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4763                 });
4764                 msgEl = bodyEl.dom.firstChild;
4765                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4766                 textboxEl.enableDisplayMode();
4767                 textboxEl.addKeyListener([10,13], function(){
4768                     if(dlg.isVisible() && opt && opt.buttons){
4769                         if(opt.buttons.ok){
4770                             handleButton("ok");
4771                         }else if(opt.buttons.yes){
4772                             handleButton("yes");
4773                         }
4774                     }
4775                 });
4776                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4777                 textareaEl.enableDisplayMode();
4778                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4779                 progressEl.enableDisplayMode();
4780                 
4781                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4782                 var pf = progressEl.dom.firstChild;
4783                 if (pf) {
4784                     pp = Roo.get(pf.firstChild);
4785                     pp.setHeight(pf.offsetHeight);
4786                 }
4787                 
4788             }
4789             return dlg;
4790         },
4791
4792         /**
4793          * Updates the message box body text
4794          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4795          * the XHTML-compliant non-breaking space character '&amp;#160;')
4796          * @return {Roo.MessageBox} This message box
4797          */
4798         updateText : function(text)
4799         {
4800             if(!dlg.isVisible() && !opt.width){
4801                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4802                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4803             }
4804             msgEl.innerHTML = text || '&#160;';
4805       
4806             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4807             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4808             var w = Math.max(
4809                     Math.min(opt.width || cw , this.maxWidth), 
4810                     Math.max(opt.minWidth || this.minWidth, bwidth)
4811             );
4812             if(opt.prompt){
4813                 activeTextEl.setWidth(w);
4814             }
4815             if(dlg.isVisible()){
4816                 dlg.fixedcenter = false;
4817             }
4818             // to big, make it scroll. = But as usual stupid IE does not support
4819             // !important..
4820             
4821             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4822                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4823                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.height = '';
4826                 bodyEl.dom.style.overflowY = '';
4827             }
4828             if (cw > w) {
4829                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4830             } else {
4831                 bodyEl.dom.style.overflowX = '';
4832             }
4833             
4834             dlg.setContentSize(w, bodyEl.getHeight());
4835             if(dlg.isVisible()){
4836                 dlg.fixedcenter = true;
4837             }
4838             return this;
4839         },
4840
4841         /**
4842          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4843          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4844          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4845          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4846          * @return {Roo.MessageBox} This message box
4847          */
4848         updateProgress : function(value, text){
4849             if(text){
4850                 this.updateText(text);
4851             }
4852             
4853             if (pp) { // weird bug on my firefox - for some reason this is not defined
4854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4855                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4856             }
4857             return this;
4858         },        
4859
4860         /**
4861          * Returns true if the message box is currently displayed
4862          * @return {Boolean} True if the message box is visible, else false
4863          */
4864         isVisible : function(){
4865             return dlg && dlg.isVisible();  
4866         },
4867
4868         /**
4869          * Hides the message box if it is displayed
4870          */
4871         hide : function(){
4872             if(this.isVisible()){
4873                 dlg.hide();
4874             }  
4875         },
4876
4877         /**
4878          * Displays a new message box, or reinitializes an existing message box, based on the config options
4879          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4880          * The following config object properties are supported:
4881          * <pre>
4882 Property    Type             Description
4883 ----------  ---------------  ------------------------------------------------------------------------------------
4884 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4885                                    closes (defaults to undefined)
4886 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4887                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4888 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4889                                    progress and wait dialogs will ignore this property and always hide the
4890                                    close button as they can only be closed programmatically.
4891 cls               String           A custom CSS class to apply to the message box element
4892 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4893                                    displayed (defaults to 75)
4894 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4895                                    function will be btn (the name of the button that was clicked, if applicable,
4896                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4897                                    Progress and wait dialogs will ignore this option since they do not respond to
4898                                    user actions and can only be closed programmatically, so any required function
4899                                    should be called by the same code after it closes the dialog.
4900 icon              String           A CSS class that provides a background image to be used as an icon for
4901                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4902 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4903 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4904 modal             Boolean          False to allow user interaction with the page while the message box is
4905                                    displayed (defaults to true)
4906 msg               String           A string that will replace the existing message box body text (defaults
4907                                    to the XHTML-compliant non-breaking space character '&#160;')
4908 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4909 progress          Boolean          True to display a progress bar (defaults to false)
4910 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4911 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4912 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4913 title             String           The title text
4914 value             String           The string value to set into the active textbox element if displayed
4915 wait              Boolean          True to display a progress bar (defaults to false)
4916 width             Number           The width of the dialog in pixels
4917 </pre>
4918          *
4919          * Example usage:
4920          * <pre><code>
4921 Roo.Msg.show({
4922    title: 'Address',
4923    msg: 'Please enter your address:',
4924    width: 300,
4925    buttons: Roo.MessageBox.OKCANCEL,
4926    multiline: true,
4927    fn: saveAddress,
4928    animEl: 'addAddressBtn'
4929 });
4930 </code></pre>
4931          * @param {Object} config Configuration options
4932          * @return {Roo.MessageBox} This message box
4933          */
4934         show : function(options)
4935         {
4936             
4937             // this causes nightmares if you show one dialog after another
4938             // especially on callbacks..
4939              
4940             if(this.isVisible()){
4941                 
4942                 this.hide();
4943                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4944                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4945                 Roo.log("New Dialog Message:" +  options.msg )
4946                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4947                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4948                 
4949             }
4950             var d = this.getDialog();
4951             opt = options;
4952             d.setTitle(opt.title || "&#160;");
4953             d.closeEl.setDisplayed(opt.closable !== false);
4954             activeTextEl = textboxEl;
4955             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4956             if(opt.prompt){
4957                 if(opt.multiline){
4958                     textboxEl.hide();
4959                     textareaEl.show();
4960                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4961                         opt.multiline : this.defaultTextHeight);
4962                     activeTextEl = textareaEl;
4963                 }else{
4964                     textboxEl.show();
4965                     textareaEl.hide();
4966                 }
4967             }else{
4968                 textboxEl.hide();
4969                 textareaEl.hide();
4970             }
4971             progressEl.setDisplayed(opt.progress === true);
4972             if (opt.progress) {
4973                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4974             }
4975             this.updateProgress(0);
4976             activeTextEl.dom.value = opt.value || "";
4977             if(opt.prompt){
4978                 dlg.setDefaultButton(activeTextEl);
4979             }else{
4980                 var bs = opt.buttons;
4981                 var db = null;
4982                 if(bs && bs.ok){
4983                     db = buttons["ok"];
4984                 }else if(bs && bs.yes){
4985                     db = buttons["yes"];
4986                 }
4987                 dlg.setDefaultButton(db);
4988             }
4989             bwidth = updateButtons(opt.buttons);
4990             this.updateText(opt.msg);
4991             if(opt.cls){
4992                 d.el.addClass(opt.cls);
4993             }
4994             d.proxyDrag = opt.proxyDrag === true;
4995             d.modal = opt.modal !== false;
4996             d.mask = opt.modal !== false ? mask : false;
4997             if(!d.isVisible()){
4998                 // force it to the end of the z-index stack so it gets a cursor in FF
4999                 document.body.appendChild(dlg.el.dom);
5000                 d.animateTarget = null;
5001                 d.show(options.animEl);
5002             }
5003             return this;
5004         },
5005
5006         /**
5007          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5008          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5009          * and closing the message box when the process is complete.
5010          * @param {String} title The title bar text
5011          * @param {String} msg The message box body text
5012          * @return {Roo.MessageBox} This message box
5013          */
5014         progress : function(title, msg){
5015             this.show({
5016                 title : title,
5017                 msg : msg,
5018                 buttons: false,
5019                 progress:true,
5020                 closable:false,
5021                 minWidth: this.minProgressWidth,
5022                 modal : true
5023             });
5024             return this;
5025         },
5026
5027         /**
5028          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5029          * If a callback function is passed it will be called after the user clicks the button, and the
5030          * id of the button that was clicked will be passed as the only parameter to the callback
5031          * (could also be the top-right close button).
5032          * @param {String} title The title bar text
5033          * @param {String} msg The message box body text
5034          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5035          * @param {Object} scope (optional) The scope of the callback function
5036          * @return {Roo.MessageBox} This message box
5037          */
5038         alert : function(title, msg, fn, scope)
5039         {
5040             this.show({
5041                 title : title,
5042                 msg : msg,
5043                 buttons: this.OK,
5044                 fn: fn,
5045                 closable : false,
5046                 scope : scope,
5047                 modal : true
5048             });
5049             return this;
5050         },
5051
5052         /**
5053          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5054          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5055          * You are responsible for closing the message box when the process is complete.
5056          * @param {String} msg The message box body text
5057          * @param {String} title (optional) The title bar text
5058          * @return {Roo.MessageBox} This message box
5059          */
5060         wait : function(msg, title){
5061             this.show({
5062                 title : title,
5063                 msg : msg,
5064                 buttons: false,
5065                 closable:false,
5066                 progress:true,
5067                 modal:true,
5068                 width:300,
5069                 wait:true
5070             });
5071             waitTimer = Roo.TaskMgr.start({
5072                 run: function(i){
5073                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5074                 },
5075                 interval: 1000
5076             });
5077             return this;
5078         },
5079
5080         /**
5081          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5082          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5083          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5084          * @param {String} title The title bar text
5085          * @param {String} msg The message box body text
5086          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5087          * @param {Object} scope (optional) The scope of the callback function
5088          * @return {Roo.MessageBox} This message box
5089          */
5090         confirm : function(title, msg, fn, scope){
5091             this.show({
5092                 title : title,
5093                 msg : msg,
5094                 buttons: this.YESNO,
5095                 fn: fn,
5096                 scope : scope,
5097                 modal : true
5098             });
5099             return this;
5100         },
5101
5102         /**
5103          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5104          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5105          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5106          * (could also be the top-right close button) and the text that was entered will be passed as the two
5107          * parameters to the callback.
5108          * @param {String} title The title bar text
5109          * @param {String} msg The message box body text
5110          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111          * @param {Object} scope (optional) The scope of the callback function
5112          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5113          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5114          * @return {Roo.MessageBox} This message box
5115          */
5116         prompt : function(title, msg, fn, scope, multiline){
5117             this.show({
5118                 title : title,
5119                 msg : msg,
5120                 buttons: this.OKCANCEL,
5121                 fn: fn,
5122                 minWidth:250,
5123                 scope : scope,
5124                 prompt:true,
5125                 multiline: multiline,
5126                 modal : true
5127             });
5128             return this;
5129         },
5130
5131         /**
5132          * Button config that displays a single OK button
5133          * @type Object
5134          */
5135         OK : {ok:true},
5136         /**
5137          * Button config that displays Yes and No buttons
5138          * @type Object
5139          */
5140         YESNO : {yes:true, no:true},
5141         /**
5142          * Button config that displays OK and Cancel buttons
5143          * @type Object
5144          */
5145         OKCANCEL : {ok:true, cancel:true},
5146         /**
5147          * Button config that displays Yes, No and Cancel buttons
5148          * @type Object
5149          */
5150         YESNOCANCEL : {yes:true, no:true, cancel:true},
5151
5152         /**
5153          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5154          * @type Number
5155          */
5156         defaultTextHeight : 75,
5157         /**
5158          * The maximum width in pixels of the message box (defaults to 600)
5159          * @type Number
5160          */
5161         maxWidth : 600,
5162         /**
5163          * The minimum width in pixels of the message box (defaults to 100)
5164          * @type Number
5165          */
5166         minWidth : 100,
5167         /**
5168          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5169          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5170          * @type Number
5171          */
5172         minProgressWidth : 250,
5173         /**
5174          * An object containing the default button text strings that can be overriden for localized language support.
5175          * Supported properties are: ok, cancel, yes and no.
5176          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5177          * @type Object
5178          */
5179         buttonText : {
5180             ok : "OK",
5181             cancel : "Cancel",
5182             yes : "Yes",
5183             no : "No"
5184         }
5185     };
5186 }();
5187
5188 /**
5189  * Shorthand for {@link Roo.MessageBox}
5190  */
5191 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5192 Roo.Msg = Roo.Msg || Roo.MessageBox;
5193 /*
5194  * - LGPL
5195  *
5196  * navbar
5197  * 
5198  */
5199
5200 /**
5201  * @class Roo.bootstrap.Navbar
5202  * @extends Roo.bootstrap.Component
5203  * Bootstrap Navbar class
5204
5205  * @constructor
5206  * Create a new Navbar
5207  * @param {Object} config The config object
5208  */
5209
5210
5211 Roo.bootstrap.Navbar = function(config){
5212     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5213     this.addEvents({
5214         // raw events
5215         /**
5216          * @event beforetoggle
5217          * Fire before toggle the menu
5218          * @param {Roo.EventObject} e
5219          */
5220         "beforetoggle" : true
5221     });
5222 };
5223
5224 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5225     
5226     
5227    
5228     // private
5229     navItems : false,
5230     loadMask : false,
5231     
5232     
5233     getAutoCreate : function(){
5234         
5235         
5236         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5237         
5238     },
5239     
5240     initEvents :function ()
5241     {
5242         //Roo.log(this.el.select('.navbar-toggle',true));
5243         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5244         
5245         var mark = {
5246             tag: "div",
5247             cls:"x-dlg-mask"
5248         };
5249         
5250         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5251         
5252         var size = this.el.getSize();
5253         this.maskEl.setSize(size.width, size.height);
5254         this.maskEl.enableDisplayMode("block");
5255         this.maskEl.hide();
5256         
5257         if(this.loadMask){
5258             this.maskEl.show();
5259         }
5260     },
5261     
5262     
5263     getChildContainer : function()
5264     {
5265         if (this.el && this.el.select('.collapse').getCount()) {
5266             return this.el.select('.collapse',true).first();
5267         }
5268         
5269         return this.el;
5270     },
5271     
5272     mask : function()
5273     {
5274         this.maskEl.show();
5275     },
5276     
5277     unmask : function()
5278     {
5279         this.maskEl.hide();
5280     },
5281     onToggle : function()
5282     {
5283         
5284         if(this.fireEvent('beforetoggle', this) === false){
5285             return;
5286         }
5287         var ce = this.el.select('.navbar-collapse',true).first();
5288       
5289         if (!ce.hasClass('show')) {
5290            this.expand();
5291         } else {
5292             this.collapse();
5293         }
5294         
5295         
5296     
5297     },
5298     /**
5299      * Expand the navbar pulldown 
5300      */
5301     expand : function ()
5302     {
5303        
5304         var ce = this.el.select('.navbar-collapse',true).first();
5305         if (ce.hasClass('collapsing')) {
5306             return;
5307         }
5308         ce.dom.style.height = '';
5309                // show it...
5310         ce.addClass('in'); // old...
5311         ce.removeClass('collapse');
5312         ce.addClass('show');
5313         var h = ce.getHeight();
5314         Roo.log(h);
5315         ce.removeClass('show');
5316         // at this point we should be able to see it..
5317         ce.addClass('collapsing');
5318         
5319         ce.setHeight(0); // resize it ...
5320         ce.on('transitionend', function() {
5321             //Roo.log('done transition');
5322             ce.removeClass('collapsing');
5323             ce.addClass('show');
5324             ce.removeClass('collapse');
5325
5326             ce.dom.style.height = '';
5327         }, this, { single: true} );
5328         ce.setHeight(h);
5329         ce.dom.scrollTop = 0;
5330     },
5331     /**
5332      * Collapse the navbar pulldown 
5333      */
5334     collapse : function()
5335     {
5336          var ce = this.el.select('.navbar-collapse',true).first();
5337        
5338         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5339             // it's collapsed or collapsing..
5340             return;
5341         }
5342         ce.removeClass('in'); // old...
5343         ce.setHeight(ce.getHeight());
5344         ce.removeClass('show');
5345         ce.addClass('collapsing');
5346         
5347         ce.on('transitionend', function() {
5348             ce.dom.style.height = '';
5349             ce.removeClass('collapsing');
5350             ce.addClass('collapse');
5351         }, this, { single: true} );
5352         ce.setHeight(0);
5353     }
5354     
5355     
5356     
5357 });
5358
5359
5360
5361  
5362
5363  /*
5364  * - LGPL
5365  *
5366  * navbar
5367  * 
5368  */
5369
5370 /**
5371  * @class Roo.bootstrap.NavSimplebar
5372  * @extends Roo.bootstrap.Navbar
5373  * Bootstrap Sidebar class
5374  *
5375  * @cfg {Boolean} inverse is inverted color
5376  * 
5377  * @cfg {String} type (nav | pills | tabs)
5378  * @cfg {Boolean} arrangement stacked | justified
5379  * @cfg {String} align (left | right) alignment
5380  * 
5381  * @cfg {Boolean} main (true|false) main nav bar? default false
5382  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5383  * 
5384  * @cfg {String} tag (header|footer|nav|div) default is nav 
5385
5386  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5387  * 
5388  * 
5389  * @constructor
5390  * Create a new Sidebar
5391  * @param {Object} config The config object
5392  */
5393
5394
5395 Roo.bootstrap.NavSimplebar = function(config){
5396     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5397 };
5398
5399 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5400     
5401     inverse: false,
5402     
5403     type: false,
5404     arrangement: '',
5405     align : false,
5406     
5407     weight : 'light',
5408     
5409     main : false,
5410     
5411     
5412     tag : false,
5413     
5414     
5415     getAutoCreate : function(){
5416         
5417         
5418         var cfg = {
5419             tag : this.tag || 'div',
5420             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5421         };
5422         if (['light','white'].indexOf(this.weight) > -1) {
5423             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5424         }
5425         cfg.cls += ' bg-' + this.weight;
5426         
5427         if (this.inverse) {
5428             cfg.cls += ' navbar-inverse';
5429             
5430         }
5431         
5432         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5433         
5434         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5435             return cfg;
5436         }
5437         
5438         
5439     
5440         
5441         cfg.cn = [
5442             {
5443                 cls: 'nav nav-' + this.xtype,
5444                 tag : 'ul'
5445             }
5446         ];
5447         
5448          
5449         this.type = this.type || 'nav';
5450         if (['tabs','pills'].indexOf(this.type) != -1) {
5451             cfg.cn[0].cls += ' nav-' + this.type
5452         
5453         
5454         } else {
5455             if (this.type!=='nav') {
5456                 Roo.log('nav type must be nav/tabs/pills')
5457             }
5458             cfg.cn[0].cls += ' navbar-nav'
5459         }
5460         
5461         
5462         
5463         
5464         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5465             cfg.cn[0].cls += ' nav-' + this.arrangement;
5466         }
5467         
5468         
5469         if (this.align === 'right') {
5470             cfg.cn[0].cls += ' navbar-right';
5471         }
5472         
5473         
5474         
5475         
5476         return cfg;
5477     
5478         
5479     }
5480     
5481     
5482     
5483 });
5484
5485
5486
5487  
5488
5489  
5490        /*
5491  * - LGPL
5492  *
5493  * navbar
5494  * navbar-fixed-top
5495  * navbar-expand-md  fixed-top 
5496  */
5497
5498 /**
5499  * @class Roo.bootstrap.NavHeaderbar
5500  * @extends Roo.bootstrap.NavSimplebar
5501  * Bootstrap Sidebar class
5502  *
5503  * @cfg {String} brand what is brand
5504  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5505  * @cfg {String} brand_href href of the brand
5506  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5507  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5508  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5509  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5510  * 
5511  * @constructor
5512  * Create a new Sidebar
5513  * @param {Object} config The config object
5514  */
5515
5516
5517 Roo.bootstrap.NavHeaderbar = function(config){
5518     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5519       
5520 };
5521
5522 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5523     
5524     position: '',
5525     brand: '',
5526     brand_href: false,
5527     srButton : true,
5528     autohide : false,
5529     desktopCenter : false,
5530    
5531     
5532     getAutoCreate : function(){
5533         
5534         var   cfg = {
5535             tag: this.nav || 'nav',
5536             cls: 'navbar navbar-expand-md',
5537             role: 'navigation',
5538             cn: []
5539         };
5540         
5541         var cn = cfg.cn;
5542         if (this.desktopCenter) {
5543             cn.push({cls : 'container', cn : []});
5544             cn = cn[0].cn;
5545         }
5546         
5547         if(this.srButton){
5548             var btn = {
5549                 tag: 'button',
5550                 type: 'button',
5551                 cls: 'navbar-toggle navbar-toggler',
5552                 'data-toggle': 'collapse',
5553                 cn: [
5554                     {
5555                         tag: 'span',
5556                         cls: 'sr-only',
5557                         html: 'Toggle navigation'
5558                     },
5559                     {
5560                         tag: 'span',
5561                         cls: 'icon-bar navbar-toggler-icon'
5562                     },
5563                     {
5564                         tag: 'span',
5565                         cls: 'icon-bar'
5566                     },
5567                     {
5568                         tag: 'span',
5569                         cls: 'icon-bar'
5570                     }
5571                 ]
5572             };
5573             
5574             cn.push( Roo.bootstrap.version == 4 ? btn : {
5575                 tag: 'div',
5576                 cls: 'navbar-header',
5577                 cn: [
5578                     btn
5579                 ]
5580             });
5581         }
5582         
5583         cn.push({
5584             tag: 'div',
5585             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5586             cn : []
5587         });
5588         
5589         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5590         
5591         if (['light','white'].indexOf(this.weight) > -1) {
5592             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5593         }
5594         cfg.cls += ' bg-' + this.weight;
5595         
5596         
5597         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5598             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5599             
5600             // tag can override this..
5601             
5602             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5603         }
5604         
5605         if (this.brand !== '') {
5606             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5607             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5608                 tag: 'a',
5609                 href: this.brand_href ? this.brand_href : '#',
5610                 cls: 'navbar-brand',
5611                 cn: [
5612                 this.brand
5613                 ]
5614             });
5615         }
5616         
5617         if(this.main){
5618             cfg.cls += ' main-nav';
5619         }
5620         
5621         
5622         return cfg;
5623
5624         
5625     },
5626     getHeaderChildContainer : function()
5627     {
5628         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5629             return this.el.select('.navbar-header',true).first();
5630         }
5631         
5632         return this.getChildContainer();
5633     },
5634     
5635     getChildContainer : function()
5636     {
5637          
5638         return this.el.select('.roo-navbar-collapse',true).first();
5639          
5640         
5641     },
5642     
5643     initEvents : function()
5644     {
5645         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5646         
5647         if (this.autohide) {
5648             
5649             var prevScroll = 0;
5650             var ft = this.el;
5651             
5652             Roo.get(document).on('scroll',function(e) {
5653                 var ns = Roo.get(document).getScroll().top;
5654                 var os = prevScroll;
5655                 prevScroll = ns;
5656                 
5657                 if(ns > os){
5658                     ft.removeClass('slideDown');
5659                     ft.addClass('slideUp');
5660                     return;
5661                 }
5662                 ft.removeClass('slideUp');
5663                 ft.addClass('slideDown');
5664                  
5665               
5666           },this);
5667         }
5668     }    
5669     
5670 });
5671
5672
5673
5674  
5675
5676  /*
5677  * - LGPL
5678  *
5679  * navbar
5680  * 
5681  */
5682
5683 /**
5684  * @class Roo.bootstrap.NavSidebar
5685  * @extends Roo.bootstrap.Navbar
5686  * Bootstrap Sidebar class
5687  * 
5688  * @constructor
5689  * Create a new Sidebar
5690  * @param {Object} config The config object
5691  */
5692
5693
5694 Roo.bootstrap.NavSidebar = function(config){
5695     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5696 };
5697
5698 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5699     
5700     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5701     
5702     getAutoCreate : function(){
5703         
5704         
5705         return  {
5706             tag: 'div',
5707             cls: 'sidebar sidebar-nav'
5708         };
5709     
5710         
5711     }
5712     
5713     
5714     
5715 });
5716
5717
5718
5719  
5720
5721  /*
5722  * - LGPL
5723  *
5724  * nav group
5725  * 
5726  */
5727
5728 /**
5729  * @class Roo.bootstrap.NavGroup
5730  * @extends Roo.bootstrap.Component
5731  * Bootstrap NavGroup class
5732  * @cfg {String} align (left|right)
5733  * @cfg {Boolean} inverse
5734  * @cfg {String} type (nav|pills|tab) default nav
5735  * @cfg {String} navId - reference Id for navbar.
5736  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5737  * 
5738  * @constructor
5739  * Create a new nav group
5740  * @param {Object} config The config object
5741  */
5742
5743 Roo.bootstrap.NavGroup = function(config){
5744     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5745     this.navItems = [];
5746    
5747     Roo.bootstrap.NavGroup.register(this);
5748      this.addEvents({
5749         /**
5750              * @event changed
5751              * Fires when the active item changes
5752              * @param {Roo.bootstrap.NavGroup} this
5753              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5754              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5755          */
5756         'changed': true
5757      });
5758     
5759 };
5760
5761 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5762     
5763     align: '',
5764     inverse: false,
5765     form: false,
5766     type: 'nav',
5767     navId : '',
5768     // private
5769     pilltype : true,
5770     
5771     navItems : false, 
5772     
5773     getAutoCreate : function()
5774     {
5775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5776         
5777         cfg = {
5778             tag : 'ul',
5779             cls: 'nav' 
5780         };
5781         if (Roo.bootstrap.version == 4) {
5782             if (['tabs','pills'].indexOf(this.type) != -1) {
5783                 cfg.cls += ' nav-' + this.type; 
5784             } else {
5785                 // trying to remove so header bar can right align top?
5786                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787                     // do not use on header bar... 
5788                     cfg.cls += ' navbar-nav';
5789                 }
5790             }
5791             
5792         } else {
5793             if (['tabs','pills'].indexOf(this.type) != -1) {
5794                 cfg.cls += ' nav-' + this.type
5795             } else {
5796                 if (this.type !== 'nav') {
5797                     Roo.log('nav type must be nav/tabs/pills')
5798                 }
5799                 cfg.cls += ' navbar-nav'
5800             }
5801         }
5802         
5803         if (this.parent() && this.parent().sidebar) {
5804             cfg = {
5805                 tag: 'ul',
5806                 cls: 'dashboard-menu sidebar-menu'
5807             };
5808             
5809             return cfg;
5810         }
5811         
5812         if (this.form === true) {
5813             cfg = {
5814                 tag: 'form',
5815                 cls: 'navbar-form form-inline'
5816             };
5817             //nav navbar-right ml-md-auto
5818             if (this.align === 'right') {
5819                 cfg.cls += ' navbar-right ml-md-auto';
5820             } else {
5821                 cfg.cls += ' navbar-left';
5822             }
5823         }
5824         
5825         if (this.align === 'right') {
5826             cfg.cls += ' navbar-right ml-md-auto';
5827         } else {
5828             cfg.cls += ' mr-auto';
5829         }
5830         
5831         if (this.inverse) {
5832             cfg.cls += ' navbar-inverse';
5833             
5834         }
5835         
5836         
5837         return cfg;
5838     },
5839     /**
5840     * sets the active Navigation item
5841     * @param {Roo.bootstrap.NavItem} the new current navitem
5842     */
5843     setActiveItem : function(item)
5844     {
5845         var prev = false;
5846         Roo.each(this.navItems, function(v){
5847             if (v == item) {
5848                 return ;
5849             }
5850             if (v.isActive()) {
5851                 v.setActive(false, true);
5852                 prev = v;
5853                 
5854             }
5855             
5856         });
5857
5858         item.setActive(true, true);
5859         this.fireEvent('changed', this, item, prev);
5860         
5861         
5862     },
5863     /**
5864     * gets the active Navigation item
5865     * @return {Roo.bootstrap.NavItem} the current navitem
5866     */
5867     getActive : function()
5868     {
5869         
5870         var prev = false;
5871         Roo.each(this.navItems, function(v){
5872             
5873             if (v.isActive()) {
5874                 prev = v;
5875                 
5876             }
5877             
5878         });
5879         return prev;
5880     },
5881     
5882     indexOfNav : function()
5883     {
5884         
5885         var prev = false;
5886         Roo.each(this.navItems, function(v,i){
5887             
5888             if (v.isActive()) {
5889                 prev = i;
5890                 
5891             }
5892             
5893         });
5894         return prev;
5895     },
5896     /**
5897     * adds a Navigation item
5898     * @param {Roo.bootstrap.NavItem} the navitem to add
5899     */
5900     addItem : function(cfg)
5901     {
5902         if (this.form && Roo.bootstrap.version == 4) {
5903             cfg.tag = 'div';
5904         }
5905         var cn = new Roo.bootstrap.NavItem(cfg);
5906         this.register(cn);
5907         cn.parentId = this.id;
5908         cn.onRender(this.el, null);
5909         return cn;
5910     },
5911     /**
5912     * register a Navigation item
5913     * @param {Roo.bootstrap.NavItem} the navitem to add
5914     */
5915     register : function(item)
5916     {
5917         this.navItems.push( item);
5918         item.navId = this.navId;
5919     
5920     },
5921     
5922     /**
5923     * clear all the Navigation item
5924     */
5925    
5926     clearAll : function()
5927     {
5928         this.navItems = [];
5929         this.el.dom.innerHTML = '';
5930     },
5931     
5932     getNavItem: function(tabId)
5933     {
5934         var ret = false;
5935         Roo.each(this.navItems, function(e) {
5936             if (e.tabId == tabId) {
5937                ret =  e;
5938                return false;
5939             }
5940             return true;
5941             
5942         });
5943         return ret;
5944     },
5945     
5946     setActiveNext : function()
5947     {
5948         var i = this.indexOfNav(this.getActive());
5949         if (i > this.navItems.length) {
5950             return;
5951         }
5952         this.setActiveItem(this.navItems[i+1]);
5953     },
5954     setActivePrev : function()
5955     {
5956         var i = this.indexOfNav(this.getActive());
5957         if (i  < 1) {
5958             return;
5959         }
5960         this.setActiveItem(this.navItems[i-1]);
5961     },
5962     clearWasActive : function(except) {
5963         Roo.each(this.navItems, function(e) {
5964             if (e.tabId != except.tabId && e.was_active) {
5965                e.was_active = false;
5966                return false;
5967             }
5968             return true;
5969             
5970         });
5971     },
5972     getWasActive : function ()
5973     {
5974         var r = false;
5975         Roo.each(this.navItems, function(e) {
5976             if (e.was_active) {
5977                r = e;
5978                return false;
5979             }
5980             return true;
5981             
5982         });
5983         return r;
5984     }
5985     
5986     
5987 });
5988
5989  
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5991     
5992     groups: {},
5993      /**
5994     * register a Navigation Group
5995     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5996     */
5997     register : function(navgrp)
5998     {
5999         this.groups[navgrp.navId] = navgrp;
6000         
6001     },
6002     /**
6003     * fetch a Navigation Group based on the navigation ID
6004     * @param {string} the navgroup to add
6005     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6006     */
6007     get: function(navId) {
6008         if (typeof(this.groups[navId]) == 'undefined') {
6009             return false;
6010             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6011         }
6012         return this.groups[navId] ;
6013     }
6014     
6015     
6016     
6017 });
6018
6019  /*
6020  * - LGPL
6021  *
6022  * row
6023  * 
6024  */
6025
6026 /**
6027  * @class Roo.bootstrap.NavItem
6028  * @extends Roo.bootstrap.Component
6029  * Bootstrap Navbar.NavItem class
6030  * @cfg {String} href  link to
6031  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6032  * @cfg {Boolean} button_outline show and outlined button
6033  * @cfg {String} html content of button
6034  * @cfg {String} badge text inside badge
6035  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036  * @cfg {String} glyphicon DEPRICATED - use fa
6037  * @cfg {String} icon DEPRICATED - use fa
6038  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039  * @cfg {Boolean} active Is item active
6040  * @cfg {Boolean} disabled Is item disabled
6041  * @cfg {String} linkcls  Link Class
6042  * @cfg {Boolean} preventDefault (true | false) default false
6043  * @cfg {String} tabId the tab that this item activates.
6044  * @cfg {String} tagtype (a|span) render as a href or span?
6045  * @cfg {Boolean} animateRef (true|false) link to element default false  
6046   
6047  * @constructor
6048  * Create a new Navbar Item
6049  * @param {Object} config The config object
6050  */
6051 Roo.bootstrap.NavItem = function(config){
6052     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6053     this.addEvents({
6054         // raw events
6055         /**
6056          * @event click
6057          * The raw click event for the entire grid.
6058          * @param {Roo.EventObject} e
6059          */
6060         "click" : true,
6061          /**
6062             * @event changed
6063             * Fires when the active item active state changes
6064             * @param {Roo.bootstrap.NavItem} this
6065             * @param {boolean} state the new state
6066              
6067          */
6068         'changed': true,
6069         /**
6070             * @event scrollto
6071             * Fires when scroll to element
6072             * @param {Roo.bootstrap.NavItem} this
6073             * @param {Object} options
6074             * @param {Roo.EventObject} e
6075              
6076          */
6077         'scrollto': true
6078     });
6079    
6080 };
6081
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6083     
6084     href: false,
6085     html: '',
6086     badge: '',
6087     icon: false,
6088     fa : false,
6089     glyphicon: false,
6090     active: false,
6091     preventDefault : false,
6092     tabId : false,
6093     tagtype : 'a',
6094     tag: 'li',
6095     disabled : false,
6096     animateRef : false,
6097     was_active : false,
6098     button_weight : '',
6099     button_outline : false,
6100     linkcls : '',
6101     navLink: false,
6102     
6103     getAutoCreate : function(){
6104          
6105         var cfg = {
6106             tag: this.tag,
6107             cls: 'nav-item'
6108         };
6109         
6110         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6111         
6112         if (this.active) {
6113             cfg.cls +=  ' active' ;
6114         }
6115         if (this.disabled) {
6116             cfg.cls += ' disabled';
6117         }
6118         
6119         // BS4 only?
6120         if (this.button_weight.length) {
6121             cfg.tag = this.href ? 'a' : 'button';
6122             cfg.html = this.html || '';
6123             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6124             if (this.href) {
6125                 cfg.href = this.href;
6126             }
6127             if (this.fa) {
6128                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6129             }
6130             
6131             // menu .. should add dropdown-menu class - so no need for carat..
6132             
6133             if (this.badge !== '') {
6134                  
6135                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6136             }
6137             return cfg;
6138         }
6139         
6140         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6141             cfg.cn = [
6142                 {
6143                     tag: this.tagtype,
6144                     href : this.href || "#",
6145                     html: this.html || ''
6146                 }
6147             ];
6148             if (this.tagtype == 'a') {
6149                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6150         
6151             }
6152             if (this.icon) {
6153                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6154             }
6155             if (this.fa) {
6156                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if(this.glyphicon) {
6159                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6160             }
6161             
6162             if (this.menu) {
6163                 
6164                 cfg.cn[0].html += " <span class='caret'></span>";
6165              
6166             }
6167             
6168             if (this.badge !== '') {
6169                  
6170                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6171             }
6172         }
6173         
6174         
6175         
6176         return cfg;
6177     },
6178     onRender : function(ct, position)
6179     {
6180        // Roo.log("Call onRender: " + this.xtype);
6181         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6182             this.tag = 'div';
6183         }
6184         
6185         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6186         this.navLink = this.el.select('.nav-link',true).first();
6187         return ret;
6188     },
6189       
6190     
6191     initEvents: function() 
6192     {
6193         if (typeof (this.menu) != 'undefined') {
6194             this.menu.parentType = this.xtype;
6195             this.menu.triggerEl = this.el;
6196             this.menu = this.addxtype(Roo.apply({}, this.menu));
6197         }
6198         
6199         this.el.on('click', this.onClick, this);
6200         
6201         //if(this.tagtype == 'span'){
6202         //    this.el.select('span',true).on('click', this.onClick, this);
6203         //}
6204        
6205         // at this point parent should be available..
6206         this.parent().register(this);
6207     },
6208     
6209     onClick : function(e)
6210     {
6211         if (e.getTarget('.dropdown-menu-item')) {
6212             // did you click on a menu itemm.... - then don't trigger onclick..
6213             return;
6214         }
6215         
6216         if(
6217                 this.preventDefault || 
6218                 this.href == '#' 
6219         ){
6220             Roo.log("NavItem - prevent Default?");
6221             e.preventDefault();
6222         }
6223         
6224         if (this.disabled) {
6225             return;
6226         }
6227         
6228         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6229         if (tg && tg.transition) {
6230             Roo.log("waiting for the transitionend");
6231             return;
6232         }
6233         
6234         
6235         
6236         //Roo.log("fire event clicked");
6237         if(this.fireEvent('click', this, e) === false){
6238             return;
6239         };
6240         
6241         if(this.tagtype == 'span'){
6242             return;
6243         }
6244         
6245         //Roo.log(this.href);
6246         var ael = this.el.select('a',true).first();
6247         //Roo.log(ael);
6248         
6249         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6250             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6251             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6252                 return; // ignore... - it's a 'hash' to another page.
6253             }
6254             Roo.log("NavItem - prevent Default?");
6255             e.preventDefault();
6256             this.scrollToElement(e);
6257         }
6258         
6259         
6260         var p =  this.parent();
6261    
6262         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6263             if (typeof(p.setActiveItem) !== 'undefined') {
6264                 p.setActiveItem(this);
6265             }
6266         }
6267         
6268         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6269         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6270             // remove the collapsed menu expand...
6271             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6272         }
6273     },
6274     
6275     isActive: function () {
6276         return this.active
6277     },
6278     setActive : function(state, fire, is_was_active)
6279     {
6280         if (this.active && !state && this.navId) {
6281             this.was_active = true;
6282             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6283             if (nv) {
6284                 nv.clearWasActive(this);
6285             }
6286             
6287         }
6288         this.active = state;
6289         
6290         if (!state ) {
6291             this.el.removeClass('active');
6292             this.navLink ? this.navLink.removeClass('active') : false;
6293         } else if (!this.el.hasClass('active')) {
6294             
6295             this.el.addClass('active');
6296             if (Roo.bootstrap.version == 4 && this.navLink ) {
6297                 this.navLink.addClass('active');
6298             }
6299             
6300         }
6301         if (fire) {
6302             this.fireEvent('changed', this, state);
6303         }
6304         
6305         // show a panel if it's registered and related..
6306         
6307         if (!this.navId || !this.tabId || !state || is_was_active) {
6308             return;
6309         }
6310         
6311         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6312         if (!tg) {
6313             return;
6314         }
6315         var pan = tg.getPanelByName(this.tabId);
6316         if (!pan) {
6317             return;
6318         }
6319         // if we can not flip to new panel - go back to old nav highlight..
6320         if (false == tg.showPanel(pan)) {
6321             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6322             if (nv) {
6323                 var onav = nv.getWasActive();
6324                 if (onav) {
6325                     onav.setActive(true, false, true);
6326                 }
6327             }
6328             
6329         }
6330         
6331         
6332         
6333     },
6334      // this should not be here...
6335     setDisabled : function(state)
6336     {
6337         this.disabled = state;
6338         if (!state ) {
6339             this.el.removeClass('disabled');
6340         } else if (!this.el.hasClass('disabled')) {
6341             this.el.addClass('disabled');
6342         }
6343         
6344     },
6345     
6346     /**
6347      * Fetch the element to display the tooltip on.
6348      * @return {Roo.Element} defaults to this.el
6349      */
6350     tooltipEl : function()
6351     {
6352         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6353     },
6354     
6355     scrollToElement : function(e)
6356     {
6357         var c = document.body;
6358         
6359         /*
6360          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6361          */
6362         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6363             c = document.documentElement;
6364         }
6365         
6366         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6367         
6368         if(!target){
6369             return;
6370         }
6371
6372         var o = target.calcOffsetsTo(c);
6373         
6374         var options = {
6375             target : target,
6376             value : o[1]
6377         };
6378         
6379         this.fireEvent('scrollto', this, options, e);
6380         
6381         Roo.get(c).scrollTo('top', options.value, true);
6382         
6383         return;
6384     }
6385 });
6386  
6387
6388  /*
6389  * - LGPL
6390  *
6391  * sidebar item
6392  *
6393  *  li
6394  *    <span> icon </span>
6395  *    <span> text </span>
6396  *    <span>badge </span>
6397  */
6398
6399 /**
6400  * @class Roo.bootstrap.NavSidebarItem
6401  * @extends Roo.bootstrap.NavItem
6402  * Bootstrap Navbar.NavSidebarItem class
6403  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6404  * {Boolean} open is the menu open
6405  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6406  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6407  * {String} buttonSize (sm|md|lg)the extra classes for the button
6408  * {Boolean} showArrow show arrow next to the text (default true)
6409  * @constructor
6410  * Create a new Navbar Button
6411  * @param {Object} config The config object
6412  */
6413 Roo.bootstrap.NavSidebarItem = function(config){
6414     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6415     this.addEvents({
6416         // raw events
6417         /**
6418          * @event click
6419          * The raw click event for the entire grid.
6420          * @param {Roo.EventObject} e
6421          */
6422         "click" : true,
6423          /**
6424             * @event changed
6425             * Fires when the active item active state changes
6426             * @param {Roo.bootstrap.NavSidebarItem} this
6427             * @param {boolean} state the new state
6428              
6429          */
6430         'changed': true
6431     });
6432    
6433 };
6434
6435 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6436     
6437     badgeWeight : 'default',
6438     
6439     open: false,
6440     
6441     buttonView : false,
6442     
6443     buttonWeight : 'default',
6444     
6445     buttonSize : 'md',
6446     
6447     showArrow : true,
6448     
6449     getAutoCreate : function(){
6450         
6451         
6452         var a = {
6453                 tag: 'a',
6454                 href : this.href || '#',
6455                 cls: '',
6456                 html : '',
6457                 cn : []
6458         };
6459         
6460         if(this.buttonView){
6461             a = {
6462                 tag: 'button',
6463                 href : this.href || '#',
6464                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6465                 html : this.html,
6466                 cn : []
6467             };
6468         }
6469         
6470         var cfg = {
6471             tag: 'li',
6472             cls: '',
6473             cn: [ a ]
6474         };
6475         
6476         if (this.active) {
6477             cfg.cls += ' active';
6478         }
6479         
6480         if (this.disabled) {
6481             cfg.cls += ' disabled';
6482         }
6483         if (this.open) {
6484             cfg.cls += ' open x-open';
6485         }
6486         // left icon..
6487         if (this.glyphicon || this.icon) {
6488             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6489             a.cn.push({ tag : 'i', cls : c }) ;
6490         }
6491         
6492         if(!this.buttonView){
6493             var span = {
6494                 tag: 'span',
6495                 html : this.html || ''
6496             };
6497
6498             a.cn.push(span);
6499             
6500         }
6501         
6502         if (this.badge !== '') {
6503             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6504         }
6505         
6506         if (this.menu) {
6507             
6508             if(this.showArrow){
6509                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6510             }
6511             
6512             a.cls += ' dropdown-toggle treeview' ;
6513         }
6514         
6515         return cfg;
6516     },
6517     
6518     initEvents : function()
6519     { 
6520         if (typeof (this.menu) != 'undefined') {
6521             this.menu.parentType = this.xtype;
6522             this.menu.triggerEl = this.el;
6523             this.menu = this.addxtype(Roo.apply({}, this.menu));
6524         }
6525         
6526         this.el.on('click', this.onClick, this);
6527         
6528         if(this.badge !== ''){
6529             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6530         }
6531         
6532     },
6533     
6534     onClick : function(e)
6535     {
6536         if(this.disabled){
6537             e.preventDefault();
6538             return;
6539         }
6540         
6541         if(this.preventDefault){
6542             e.preventDefault();
6543         }
6544         
6545         this.fireEvent('click', this, e);
6546     },
6547     
6548     disable : function()
6549     {
6550         this.setDisabled(true);
6551     },
6552     
6553     enable : function()
6554     {
6555         this.setDisabled(false);
6556     },
6557     
6558     setDisabled : function(state)
6559     {
6560         if(this.disabled == state){
6561             return;
6562         }
6563         
6564         this.disabled = state;
6565         
6566         if (state) {
6567             this.el.addClass('disabled');
6568             return;
6569         }
6570         
6571         this.el.removeClass('disabled');
6572         
6573         return;
6574     },
6575     
6576     setActive : function(state)
6577     {
6578         if(this.active == state){
6579             return;
6580         }
6581         
6582         this.active = state;
6583         
6584         if (state) {
6585             this.el.addClass('active');
6586             return;
6587         }
6588         
6589         this.el.removeClass('active');
6590         
6591         return;
6592     },
6593     
6594     isActive: function () 
6595     {
6596         return this.active;
6597     },
6598     
6599     setBadge : function(str)
6600     {
6601         if(!this.badgeEl){
6602             return;
6603         }
6604         
6605         this.badgeEl.dom.innerHTML = str;
6606     }
6607     
6608    
6609      
6610  
6611 });
6612  
6613
6614  /*
6615  * - LGPL
6616  *
6617  *  Breadcrumb Nav
6618  * 
6619  */
6620 Roo.namespace('Roo.bootstrap.breadcrumb');
6621
6622
6623 /**
6624  * @class Roo.bootstrap.breadcrumb.Nav
6625  * @extends Roo.bootstrap.Component
6626  * Bootstrap Breadcrumb Nav Class
6627  *  
6628  * @children Roo.bootstrap.breadcrumb.Item
6629  * 
6630  * @constructor
6631  * Create a new breadcrumb.Nav
6632  * @param {Object} config The config object
6633  */
6634
6635
6636 Roo.bootstrap.breadcrumb.Nav = function(config){
6637     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6638     
6639     
6640 };
6641
6642 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6643     
6644     getAutoCreate : function()
6645     {
6646
6647         var cfg = {
6648             tag: 'nav',
6649             cn : [
6650                 {
6651                     tag : 'ol',
6652                     cls : 'breadcrumb'
6653                 }
6654             ]
6655             
6656         };
6657           
6658         return cfg;
6659     },
6660     
6661     initEvents: function()
6662     {
6663         this.olEl = this.el.select('ol',true).first();    
6664     },
6665     getChildContainer : function()
6666     {
6667         return this.olEl;  
6668     }
6669     
6670 });
6671
6672  /*
6673  * - LGPL
6674  *
6675  *  Breadcrumb Item
6676  * 
6677  */
6678
6679
6680 /**
6681  * @class Roo.bootstrap.breadcrumb.Nav
6682  * @extends Roo.bootstrap.Component
6683  * Bootstrap Breadcrumb Nav Class
6684  *  
6685  * @children Roo.bootstrap.breadcrumb.Component
6686  * @cfg {String} html the content of the link.
6687  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6688  * @cfg {Boolean} active is it active
6689
6690  * 
6691  * @constructor
6692  * Create a new breadcrumb.Nav
6693  * @param {Object} config The config object
6694  */
6695
6696 Roo.bootstrap.breadcrumb.Item = function(config){
6697     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6698     this.addEvents({
6699         // img events
6700         /**
6701          * @event click
6702          * The img click event for the img.
6703          * @param {Roo.EventObject} e
6704          */
6705         "click" : true
6706     });
6707     
6708 };
6709
6710 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6711     
6712     href: false,
6713     html : '',
6714     
6715     getAutoCreate : function()
6716     {
6717
6718         var cfg = {
6719             tag: 'li',
6720             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6721         };
6722         if (this.href !== false) {
6723             cfg.cn = [{
6724                 tag : 'a',
6725                 href : this.href,
6726                 html : this.html
6727             }];
6728         } else {
6729             cfg.html = this.html;
6730         }
6731         
6732         return cfg;
6733     },
6734     
6735     initEvents: function()
6736     {
6737         if (this.href) {
6738             this.el.select('a', true).first().on('click',this.onClick, this)
6739         }
6740         
6741     },
6742     onClick : function(e)
6743     {
6744         e.preventDefault();
6745         this.fireEvent('click',this,  e);
6746     }
6747     
6748 });
6749
6750  /*
6751  * - LGPL
6752  *
6753  * row
6754  * 
6755  */
6756
6757 /**
6758  * @class Roo.bootstrap.Row
6759  * @extends Roo.bootstrap.Component
6760  * Bootstrap Row class (contains columns...)
6761  * 
6762  * @constructor
6763  * Create a new Row
6764  * @param {Object} config The config object
6765  */
6766
6767 Roo.bootstrap.Row = function(config){
6768     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6769 };
6770
6771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6772     
6773     getAutoCreate : function(){
6774        return {
6775             cls: 'row clearfix'
6776        };
6777     }
6778     
6779     
6780 });
6781
6782  
6783
6784  /*
6785  * - LGPL
6786  *
6787  * pagination
6788  * 
6789  */
6790
6791 /**
6792  * @class Roo.bootstrap.Pagination
6793  * @extends Roo.bootstrap.Component
6794  * Bootstrap Pagination class
6795  * @cfg {String} size xs | sm | md | lg
6796  * @cfg {Boolean} inverse false | true
6797  * 
6798  * @constructor
6799  * Create a new Pagination
6800  * @param {Object} config The config object
6801  */
6802
6803 Roo.bootstrap.Pagination = function(config){
6804     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6805 };
6806
6807 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6808     
6809     cls: false,
6810     size: false,
6811     inverse: false,
6812     
6813     getAutoCreate : function(){
6814         var cfg = {
6815             tag: 'ul',
6816                 cls: 'pagination'
6817         };
6818         if (this.inverse) {
6819             cfg.cls += ' inverse';
6820         }
6821         if (this.html) {
6822             cfg.html=this.html;
6823         }
6824         if (this.cls) {
6825             cfg.cls += " " + this.cls;
6826         }
6827         return cfg;
6828     }
6829    
6830 });
6831
6832  
6833
6834  /*
6835  * - LGPL
6836  *
6837  * Pagination item
6838  * 
6839  */
6840
6841
6842 /**
6843  * @class Roo.bootstrap.PaginationItem
6844  * @extends Roo.bootstrap.Component
6845  * Bootstrap PaginationItem class
6846  * @cfg {String} html text
6847  * @cfg {String} href the link
6848  * @cfg {Boolean} preventDefault (true | false) default true
6849  * @cfg {Boolean} active (true | false) default false
6850  * @cfg {Boolean} disabled default false
6851  * 
6852  * 
6853  * @constructor
6854  * Create a new PaginationItem
6855  * @param {Object} config The config object
6856  */
6857
6858
6859 Roo.bootstrap.PaginationItem = function(config){
6860     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6861     this.addEvents({
6862         // raw events
6863         /**
6864          * @event click
6865          * The raw click event for the entire grid.
6866          * @param {Roo.EventObject} e
6867          */
6868         "click" : true
6869     });
6870 };
6871
6872 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6873     
6874     href : false,
6875     html : false,
6876     preventDefault: true,
6877     active : false,
6878     cls : false,
6879     disabled: false,
6880     
6881     getAutoCreate : function(){
6882         var cfg= {
6883             tag: 'li',
6884             cn: [
6885                 {
6886                     tag : 'a',
6887                     href : this.href ? this.href : '#',
6888                     html : this.html ? this.html : ''
6889                 }
6890             ]
6891         };
6892         
6893         if(this.cls){
6894             cfg.cls = this.cls;
6895         }
6896         
6897         if(this.disabled){
6898             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6899         }
6900         
6901         if(this.active){
6902             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6903         }
6904         
6905         return cfg;
6906     },
6907     
6908     initEvents: function() {
6909         
6910         this.el.on('click', this.onClick, this);
6911         
6912     },
6913     onClick : function(e)
6914     {
6915         Roo.log('PaginationItem on click ');
6916         if(this.preventDefault){
6917             e.preventDefault();
6918         }
6919         
6920         if(this.disabled){
6921             return;
6922         }
6923         
6924         this.fireEvent('click', this, e);
6925     }
6926    
6927 });
6928
6929  
6930
6931  /*
6932  * - LGPL
6933  *
6934  * slider
6935  * 
6936  */
6937
6938
6939 /**
6940  * @class Roo.bootstrap.Slider
6941  * @extends Roo.bootstrap.Component
6942  * Bootstrap Slider class
6943  *    
6944  * @constructor
6945  * Create a new Slider
6946  * @param {Object} config The config object
6947  */
6948
6949 Roo.bootstrap.Slider = function(config){
6950     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6951 };
6952
6953 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6954     
6955     getAutoCreate : function(){
6956         
6957         var cfg = {
6958             tag: 'div',
6959             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6960             cn: [
6961                 {
6962                     tag: 'a',
6963                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6964                 }
6965             ]
6966         };
6967         
6968         return cfg;
6969     }
6970    
6971 });
6972
6973  /*
6974  * Based on:
6975  * Ext JS Library 1.1.1
6976  * Copyright(c) 2006-2007, Ext JS, LLC.
6977  *
6978  * Originally Released Under LGPL - original licence link has changed is not relivant.
6979  *
6980  * Fork - LGPL
6981  * <script type="text/javascript">
6982  */
6983  
6984
6985 /**
6986  * @class Roo.grid.ColumnModel
6987  * @extends Roo.util.Observable
6988  * This is the default implementation of a ColumnModel used by the Grid. It defines
6989  * the columns in the grid.
6990  * <br>Usage:<br>
6991  <pre><code>
6992  var colModel = new Roo.grid.ColumnModel([
6993         {header: "Ticker", width: 60, sortable: true, locked: true},
6994         {header: "Company Name", width: 150, sortable: true},
6995         {header: "Market Cap.", width: 100, sortable: true},
6996         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6997         {header: "Employees", width: 100, sortable: true, resizable: false}
6998  ]);
6999  </code></pre>
7000  * <p>
7001  
7002  * The config options listed for this class are options which may appear in each
7003  * individual column definition.
7004  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7005  * @constructor
7006  * @param {Object} config An Array of column config objects. See this class's
7007  * config objects for details.
7008 */
7009 Roo.grid.ColumnModel = function(config){
7010         /**
7011      * The config passed into the constructor
7012      */
7013     this.config = config;
7014     this.lookup = {};
7015
7016     // if no id, create one
7017     // if the column does not have a dataIndex mapping,
7018     // map it to the order it is in the config
7019     for(var i = 0, len = config.length; i < len; i++){
7020         var c = config[i];
7021         if(typeof c.dataIndex == "undefined"){
7022             c.dataIndex = i;
7023         }
7024         if(typeof c.renderer == "string"){
7025             c.renderer = Roo.util.Format[c.renderer];
7026         }
7027         if(typeof c.id == "undefined"){
7028             c.id = Roo.id();
7029         }
7030         if(c.editor && c.editor.xtype){
7031             c.editor  = Roo.factory(c.editor, Roo.grid);
7032         }
7033         if(c.editor && c.editor.isFormField){
7034             c.editor = new Roo.grid.GridEditor(c.editor);
7035         }
7036         this.lookup[c.id] = c;
7037     }
7038
7039     /**
7040      * The width of columns which have no width specified (defaults to 100)
7041      * @type Number
7042      */
7043     this.defaultWidth = 100;
7044
7045     /**
7046      * Default sortable of columns which have no sortable specified (defaults to false)
7047      * @type Boolean
7048      */
7049     this.defaultSortable = false;
7050
7051     this.addEvents({
7052         /**
7053              * @event widthchange
7054              * Fires when the width of a column changes.
7055              * @param {ColumnModel} this
7056              * @param {Number} columnIndex The column index
7057              * @param {Number} newWidth The new width
7058              */
7059             "widthchange": true,
7060         /**
7061              * @event headerchange
7062              * Fires when the text of a header changes.
7063              * @param {ColumnModel} this
7064              * @param {Number} columnIndex The column index
7065              * @param {Number} newText The new header text
7066              */
7067             "headerchange": true,
7068         /**
7069              * @event hiddenchange
7070              * Fires when a column is hidden or "unhidden".
7071              * @param {ColumnModel} this
7072              * @param {Number} columnIndex The column index
7073              * @param {Boolean} hidden true if hidden, false otherwise
7074              */
7075             "hiddenchange": true,
7076             /**
7077          * @event columnmoved
7078          * Fires when a column is moved.
7079          * @param {ColumnModel} this
7080          * @param {Number} oldIndex
7081          * @param {Number} newIndex
7082          */
7083         "columnmoved" : true,
7084         /**
7085          * @event columlockchange
7086          * Fires when a column's locked state is changed
7087          * @param {ColumnModel} this
7088          * @param {Number} colIndex
7089          * @param {Boolean} locked true if locked
7090          */
7091         "columnlockchange" : true
7092     });
7093     Roo.grid.ColumnModel.superclass.constructor.call(this);
7094 };
7095 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7096     /**
7097      * @cfg {String} header The header text to display in the Grid view.
7098      */
7099     /**
7100      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7101      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7102      * specified, the column's index is used as an index into the Record's data Array.
7103      */
7104     /**
7105      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7106      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7107      */
7108     /**
7109      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7110      * Defaults to the value of the {@link #defaultSortable} property.
7111      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7112      */
7113     /**
7114      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7115      */
7116     /**
7117      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7121      */
7122     /**
7123      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7124      */
7125     /**
7126      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7127      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7128      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7129      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7130      */
7131        /**
7132      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7133      */
7134     /**
7135      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7136      */
7137     /**
7138      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} cursor (Optional)
7142      */
7143     /**
7144      * @cfg {String} tooltip (Optional)
7145      */
7146     /**
7147      * @cfg {Number} xs (Optional)
7148      */
7149     /**
7150      * @cfg {Number} sm (Optional)
7151      */
7152     /**
7153      * @cfg {Number} md (Optional)
7154      */
7155     /**
7156      * @cfg {Number} lg (Optional)
7157      */
7158     /**
7159      * Returns the id of the column at the specified index.
7160      * @param {Number} index The column index
7161      * @return {String} the id
7162      */
7163     getColumnId : function(index){
7164         return this.config[index].id;
7165     },
7166
7167     /**
7168      * Returns the column for a specified id.
7169      * @param {String} id The column id
7170      * @return {Object} the column
7171      */
7172     getColumnById : function(id){
7173         return this.lookup[id];
7174     },
7175
7176     
7177     /**
7178      * Returns the column for a specified dataIndex.
7179      * @param {String} dataIndex The column dataIndex
7180      * @return {Object|Boolean} the column or false if not found
7181      */
7182     getColumnByDataIndex: function(dataIndex){
7183         var index = this.findColumnIndex(dataIndex);
7184         return index > -1 ? this.config[index] : false;
7185     },
7186     
7187     /**
7188      * Returns the index for a specified column id.
7189      * @param {String} id The column id
7190      * @return {Number} the index, or -1 if not found
7191      */
7192     getIndexById : function(id){
7193         for(var i = 0, len = this.config.length; i < len; i++){
7194             if(this.config[i].id == id){
7195                 return i;
7196             }
7197         }
7198         return -1;
7199     },
7200     
7201     /**
7202      * Returns the index for a specified column dataIndex.
7203      * @param {String} dataIndex The column dataIndex
7204      * @return {Number} the index, or -1 if not found
7205      */
7206     
7207     findColumnIndex : function(dataIndex){
7208         for(var i = 0, len = this.config.length; i < len; i++){
7209             if(this.config[i].dataIndex == dataIndex){
7210                 return i;
7211             }
7212         }
7213         return -1;
7214     },
7215     
7216     
7217     moveColumn : function(oldIndex, newIndex){
7218         var c = this.config[oldIndex];
7219         this.config.splice(oldIndex, 1);
7220         this.config.splice(newIndex, 0, c);
7221         this.dataMap = null;
7222         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7223     },
7224
7225     isLocked : function(colIndex){
7226         return this.config[colIndex].locked === true;
7227     },
7228
7229     setLocked : function(colIndex, value, suppressEvent){
7230         if(this.isLocked(colIndex) == value){
7231             return;
7232         }
7233         this.config[colIndex].locked = value;
7234         if(!suppressEvent){
7235             this.fireEvent("columnlockchange", this, colIndex, value);
7236         }
7237     },
7238
7239     getTotalLockedWidth : function(){
7240         var totalWidth = 0;
7241         for(var i = 0; i < this.config.length; i++){
7242             if(this.isLocked(i) && !this.isHidden(i)){
7243                 this.totalWidth += this.getColumnWidth(i);
7244             }
7245         }
7246         return totalWidth;
7247     },
7248
7249     getLockedCount : function(){
7250         for(var i = 0, len = this.config.length; i < len; i++){
7251             if(!this.isLocked(i)){
7252                 return i;
7253             }
7254         }
7255         
7256         return this.config.length;
7257     },
7258
7259     /**
7260      * Returns the number of columns.
7261      * @return {Number}
7262      */
7263     getColumnCount : function(visibleOnly){
7264         if(visibleOnly === true){
7265             var c = 0;
7266             for(var i = 0, len = this.config.length; i < len; i++){
7267                 if(!this.isHidden(i)){
7268                     c++;
7269                 }
7270             }
7271             return c;
7272         }
7273         return this.config.length;
7274     },
7275
7276     /**
7277      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7278      * @param {Function} fn
7279      * @param {Object} scope (optional)
7280      * @return {Array} result
7281      */
7282     getColumnsBy : function(fn, scope){
7283         var r = [];
7284         for(var i = 0, len = this.config.length; i < len; i++){
7285             var c = this.config[i];
7286             if(fn.call(scope||this, c, i) === true){
7287                 r[r.length] = c;
7288             }
7289         }
7290         return r;
7291     },
7292
7293     /**
7294      * Returns true if the specified column is sortable.
7295      * @param {Number} col The column index
7296      * @return {Boolean}
7297      */
7298     isSortable : function(col){
7299         if(typeof this.config[col].sortable == "undefined"){
7300             return this.defaultSortable;
7301         }
7302         return this.config[col].sortable;
7303     },
7304
7305     /**
7306      * Returns the rendering (formatting) function defined for the column.
7307      * @param {Number} col The column index.
7308      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7309      */
7310     getRenderer : function(col){
7311         if(!this.config[col].renderer){
7312             return Roo.grid.ColumnModel.defaultRenderer;
7313         }
7314         return this.config[col].renderer;
7315     },
7316
7317     /**
7318      * Sets the rendering (formatting) function for a column.
7319      * @param {Number} col The column index
7320      * @param {Function} fn The function to use to process the cell's raw data
7321      * to return HTML markup for the grid view. The render function is called with
7322      * the following parameters:<ul>
7323      * <li>Data value.</li>
7324      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7325      * <li>css A CSS style string to apply to the table cell.</li>
7326      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7327      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7328      * <li>Row index</li>
7329      * <li>Column index</li>
7330      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7331      */
7332     setRenderer : function(col, fn){
7333         this.config[col].renderer = fn;
7334     },
7335
7336     /**
7337      * Returns the width for the specified column.
7338      * @param {Number} col The column index
7339      * @return {Number}
7340      */
7341     getColumnWidth : function(col){
7342         return this.config[col].width * 1 || this.defaultWidth;
7343     },
7344
7345     /**
7346      * Sets the width for a column.
7347      * @param {Number} col The column index
7348      * @param {Number} width The new width
7349      */
7350     setColumnWidth : function(col, width, suppressEvent){
7351         this.config[col].width = width;
7352         this.totalWidth = null;
7353         if(!suppressEvent){
7354              this.fireEvent("widthchange", this, col, width);
7355         }
7356     },
7357
7358     /**
7359      * Returns the total width of all columns.
7360      * @param {Boolean} includeHidden True to include hidden column widths
7361      * @return {Number}
7362      */
7363     getTotalWidth : function(includeHidden){
7364         if(!this.totalWidth){
7365             this.totalWidth = 0;
7366             for(var i = 0, len = this.config.length; i < len; i++){
7367                 if(includeHidden || !this.isHidden(i)){
7368                     this.totalWidth += this.getColumnWidth(i);
7369                 }
7370             }
7371         }
7372         return this.totalWidth;
7373     },
7374
7375     /**
7376      * Returns the header for the specified column.
7377      * @param {Number} col The column index
7378      * @return {String}
7379      */
7380     getColumnHeader : function(col){
7381         return this.config[col].header;
7382     },
7383
7384     /**
7385      * Sets the header for a column.
7386      * @param {Number} col The column index
7387      * @param {String} header The new header
7388      */
7389     setColumnHeader : function(col, header){
7390         this.config[col].header = header;
7391         this.fireEvent("headerchange", this, col, header);
7392     },
7393
7394     /**
7395      * Returns the tooltip for the specified column.
7396      * @param {Number} col The column index
7397      * @return {String}
7398      */
7399     getColumnTooltip : function(col){
7400             return this.config[col].tooltip;
7401     },
7402     /**
7403      * Sets the tooltip for a column.
7404      * @param {Number} col The column index
7405      * @param {String} tooltip The new tooltip
7406      */
7407     setColumnTooltip : function(col, tooltip){
7408             this.config[col].tooltip = tooltip;
7409     },
7410
7411     /**
7412      * Returns the dataIndex for the specified column.
7413      * @param {Number} col The column index
7414      * @return {Number}
7415      */
7416     getDataIndex : function(col){
7417         return this.config[col].dataIndex;
7418     },
7419
7420     /**
7421      * Sets the dataIndex for a column.
7422      * @param {Number} col The column index
7423      * @param {Number} dataIndex The new dataIndex
7424      */
7425     setDataIndex : function(col, dataIndex){
7426         this.config[col].dataIndex = dataIndex;
7427     },
7428
7429     
7430     
7431     /**
7432      * Returns true if the cell is editable.
7433      * @param {Number} colIndex The column index
7434      * @param {Number} rowIndex The row index - this is nto actually used..?
7435      * @return {Boolean}
7436      */
7437     isCellEditable : function(colIndex, rowIndex){
7438         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7439     },
7440
7441     /**
7442      * Returns the editor defined for the cell/column.
7443      * return false or null to disable editing.
7444      * @param {Number} colIndex The column index
7445      * @param {Number} rowIndex The row index
7446      * @return {Object}
7447      */
7448     getCellEditor : function(colIndex, rowIndex){
7449         return this.config[colIndex].editor;
7450     },
7451
7452     /**
7453      * Sets if a column is editable.
7454      * @param {Number} col The column index
7455      * @param {Boolean} editable True if the column is editable
7456      */
7457     setEditable : function(col, editable){
7458         this.config[col].editable = editable;
7459     },
7460
7461
7462     /**
7463      * Returns true if the column is hidden.
7464      * @param {Number} colIndex The column index
7465      * @return {Boolean}
7466      */
7467     isHidden : function(colIndex){
7468         return this.config[colIndex].hidden;
7469     },
7470
7471
7472     /**
7473      * Returns true if the column width cannot be changed
7474      */
7475     isFixed : function(colIndex){
7476         return this.config[colIndex].fixed;
7477     },
7478
7479     /**
7480      * Returns true if the column can be resized
7481      * @return {Boolean}
7482      */
7483     isResizable : function(colIndex){
7484         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7485     },
7486     /**
7487      * Sets if a column is hidden.
7488      * @param {Number} colIndex The column index
7489      * @param {Boolean} hidden True if the column is hidden
7490      */
7491     setHidden : function(colIndex, hidden){
7492         this.config[colIndex].hidden = hidden;
7493         this.totalWidth = null;
7494         this.fireEvent("hiddenchange", this, colIndex, hidden);
7495     },
7496
7497     /**
7498      * Sets the editor for a column.
7499      * @param {Number} col The column index
7500      * @param {Object} editor The editor object
7501      */
7502     setEditor : function(col, editor){
7503         this.config[col].editor = editor;
7504     }
7505 });
7506
7507 Roo.grid.ColumnModel.defaultRenderer = function(value)
7508 {
7509     if(typeof value == "object") {
7510         return value;
7511     }
7512         if(typeof value == "string" && value.length < 1){
7513             return "&#160;";
7514         }
7515     
7516         return String.format("{0}", value);
7517 };
7518
7519 // Alias for backwards compatibility
7520 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7521 /*
7522  * Based on:
7523  * Ext JS Library 1.1.1
7524  * Copyright(c) 2006-2007, Ext JS, LLC.
7525  *
7526  * Originally Released Under LGPL - original licence link has changed is not relivant.
7527  *
7528  * Fork - LGPL
7529  * <script type="text/javascript">
7530  */
7531  
7532 /**
7533  * @class Roo.LoadMask
7534  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7535  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7536  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7537  * element's UpdateManager load indicator and will be destroyed after the initial load.
7538  * @constructor
7539  * Create a new LoadMask
7540  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7541  * @param {Object} config The config object
7542  */
7543 Roo.LoadMask = function(el, config){
7544     this.el = Roo.get(el);
7545     Roo.apply(this, config);
7546     if(this.store){
7547         this.store.on('beforeload', this.onBeforeLoad, this);
7548         this.store.on('load', this.onLoad, this);
7549         this.store.on('loadexception', this.onLoadException, this);
7550         this.removeMask = false;
7551     }else{
7552         var um = this.el.getUpdateManager();
7553         um.showLoadIndicator = false; // disable the default indicator
7554         um.on('beforeupdate', this.onBeforeLoad, this);
7555         um.on('update', this.onLoad, this);
7556         um.on('failure', this.onLoad, this);
7557         this.removeMask = true;
7558     }
7559 };
7560
7561 Roo.LoadMask.prototype = {
7562     /**
7563      * @cfg {Boolean} removeMask
7564      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7565      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7566      */
7567     /**
7568      * @cfg {String} msg
7569      * The text to display in a centered loading message box (defaults to 'Loading...')
7570      */
7571     msg : 'Loading...',
7572     /**
7573      * @cfg {String} msgCls
7574      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7575      */
7576     msgCls : 'x-mask-loading',
7577
7578     /**
7579      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7580      * @type Boolean
7581      */
7582     disabled: false,
7583
7584     /**
7585      * Disables the mask to prevent it from being displayed
7586      */
7587     disable : function(){
7588        this.disabled = true;
7589     },
7590
7591     /**
7592      * Enables the mask so that it can be displayed
7593      */
7594     enable : function(){
7595         this.disabled = false;
7596     },
7597     
7598     onLoadException : function()
7599     {
7600         Roo.log(arguments);
7601         
7602         if (typeof(arguments[3]) != 'undefined') {
7603             Roo.MessageBox.alert("Error loading",arguments[3]);
7604         } 
7605         /*
7606         try {
7607             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7608                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7609             }   
7610         } catch(e) {
7611             
7612         }
7613         */
7614     
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617     // private
7618     onLoad : function()
7619     {
7620         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7621     },
7622
7623     // private
7624     onBeforeLoad : function(){
7625         if(!this.disabled){
7626             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7627         }
7628     },
7629
7630     // private
7631     destroy : function(){
7632         if(this.store){
7633             this.store.un('beforeload', this.onBeforeLoad, this);
7634             this.store.un('load', this.onLoad, this);
7635             this.store.un('loadexception', this.onLoadException, this);
7636         }else{
7637             var um = this.el.getUpdateManager();
7638             um.un('beforeupdate', this.onBeforeLoad, this);
7639             um.un('update', this.onLoad, this);
7640             um.un('failure', this.onLoad, this);
7641         }
7642     }
7643 };/*
7644  * - LGPL
7645  *
7646  * table
7647  * 
7648  */
7649
7650 /**
7651  * @class Roo.bootstrap.Table
7652  * @extends Roo.bootstrap.Component
7653  * Bootstrap Table class
7654  * @cfg {String} cls table class
7655  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7656  * @cfg {String} bgcolor Specifies the background color for a table
7657  * @cfg {Number} border Specifies whether the table cells should have borders or not
7658  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7659  * @cfg {Number} cellspacing Specifies the space between cells
7660  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7661  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7662  * @cfg {String} sortable Specifies that the table should be sortable
7663  * @cfg {String} summary Specifies a summary of the content of a table
7664  * @cfg {Number} width Specifies the width of a table
7665  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7666  * 
7667  * @cfg {boolean} striped Should the rows be alternative striped
7668  * @cfg {boolean} bordered Add borders to the table
7669  * @cfg {boolean} hover Add hover highlighting
7670  * @cfg {boolean} condensed Format condensed
7671  * @cfg {boolean} responsive Format condensed
7672  * @cfg {Boolean} loadMask (true|false) default false
7673  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7674  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7675  * @cfg {Boolean} rowSelection (true|false) default false
7676  * @cfg {Boolean} cellSelection (true|false) default false
7677  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7678  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7679  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7680  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7681  
7682  * 
7683  * @constructor
7684  * Create a new Table
7685  * @param {Object} config The config object
7686  */
7687
7688 Roo.bootstrap.Table = function(config){
7689     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7690     
7691   
7692     
7693     // BC...
7694     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7695     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7696     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7697     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7698     
7699     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7700     if (this.sm) {
7701         this.sm.grid = this;
7702         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7703         this.sm = this.selModel;
7704         this.sm.xmodule = this.xmodule || false;
7705     }
7706     
7707     if (this.cm && typeof(this.cm.config) == 'undefined') {
7708         this.colModel = new Roo.grid.ColumnModel(this.cm);
7709         this.cm = this.colModel;
7710         this.cm.xmodule = this.xmodule || false;
7711     }
7712     if (this.store) {
7713         this.store= Roo.factory(this.store, Roo.data);
7714         this.ds = this.store;
7715         this.ds.xmodule = this.xmodule || false;
7716          
7717     }
7718     if (this.footer && this.store) {
7719         this.footer.dataSource = this.ds;
7720         this.footer = Roo.factory(this.footer);
7721     }
7722     
7723     /** @private */
7724     this.addEvents({
7725         /**
7726          * @event cellclick
7727          * Fires when a cell is clicked
7728          * @param {Roo.bootstrap.Table} this
7729          * @param {Roo.Element} el
7730          * @param {Number} rowIndex
7731          * @param {Number} columnIndex
7732          * @param {Roo.EventObject} e
7733          */
7734         "cellclick" : true,
7735         /**
7736          * @event celldblclick
7737          * Fires when a cell is double clicked
7738          * @param {Roo.bootstrap.Table} this
7739          * @param {Roo.Element} el
7740          * @param {Number} rowIndex
7741          * @param {Number} columnIndex
7742          * @param {Roo.EventObject} e
7743          */
7744         "celldblclick" : true,
7745         /**
7746          * @event rowclick
7747          * Fires when a row is clicked
7748          * @param {Roo.bootstrap.Table} this
7749          * @param {Roo.Element} el
7750          * @param {Number} rowIndex
7751          * @param {Roo.EventObject} e
7752          */
7753         "rowclick" : true,
7754         /**
7755          * @event rowdblclick
7756          * Fires when a row is double clicked
7757          * @param {Roo.bootstrap.Table} this
7758          * @param {Roo.Element} el
7759          * @param {Number} rowIndex
7760          * @param {Roo.EventObject} e
7761          */
7762         "rowdblclick" : true,
7763         /**
7764          * @event mouseover
7765          * Fires when a mouseover occur
7766          * @param {Roo.bootstrap.Table} this
7767          * @param {Roo.Element} el
7768          * @param {Number} rowIndex
7769          * @param {Number} columnIndex
7770          * @param {Roo.EventObject} e
7771          */
7772         "mouseover" : true,
7773         /**
7774          * @event mouseout
7775          * Fires when a mouseout occur
7776          * @param {Roo.bootstrap.Table} this
7777          * @param {Roo.Element} el
7778          * @param {Number} rowIndex
7779          * @param {Number} columnIndex
7780          * @param {Roo.EventObject} e
7781          */
7782         "mouseout" : true,
7783         /**
7784          * @event rowclass
7785          * Fires when a row is rendered, so you can change add a style to it.
7786          * @param {Roo.bootstrap.Table} this
7787          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7788          */
7789         'rowclass' : true,
7790           /**
7791          * @event rowsrendered
7792          * Fires when all the  rows have been rendered
7793          * @param {Roo.bootstrap.Table} this
7794          */
7795         'rowsrendered' : true,
7796         /**
7797          * @event contextmenu
7798          * The raw contextmenu event for the entire grid.
7799          * @param {Roo.EventObject} e
7800          */
7801         "contextmenu" : true,
7802         /**
7803          * @event rowcontextmenu
7804          * Fires when a row is right clicked
7805          * @param {Roo.bootstrap.Table} this
7806          * @param {Number} rowIndex
7807          * @param {Roo.EventObject} e
7808          */
7809         "rowcontextmenu" : true,
7810         /**
7811          * @event cellcontextmenu
7812          * Fires when a cell is right clicked
7813          * @param {Roo.bootstrap.Table} this
7814          * @param {Number} rowIndex
7815          * @param {Number} cellIndex
7816          * @param {Roo.EventObject} e
7817          */
7818          "cellcontextmenu" : true,
7819          /**
7820          * @event headercontextmenu
7821          * Fires when a header is right clicked
7822          * @param {Roo.bootstrap.Table} this
7823          * @param {Number} columnIndex
7824          * @param {Roo.EventObject} e
7825          */
7826         "headercontextmenu" : true
7827     });
7828 };
7829
7830 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7831     
7832     cls: false,
7833     align: false,
7834     bgcolor: false,
7835     border: false,
7836     cellpadding: false,
7837     cellspacing: false,
7838     frame: false,
7839     rules: false,
7840     sortable: false,
7841     summary: false,
7842     width: false,
7843     striped : false,
7844     scrollBody : false,
7845     bordered: false,
7846     hover:  false,
7847     condensed : false,
7848     responsive : false,
7849     sm : false,
7850     cm : false,
7851     store : false,
7852     loadMask : false,
7853     footerShow : true,
7854     headerShow : true,
7855   
7856     rowSelection : false,
7857     cellSelection : false,
7858     layout : false,
7859     
7860     // Roo.Element - the tbody
7861     mainBody: false,
7862     // Roo.Element - thead element
7863     mainHead: false,
7864     
7865     container: false, // used by gridpanel...
7866     
7867     lazyLoad : false,
7868     
7869     CSS : Roo.util.CSS,
7870     
7871     auto_hide_footer : false,
7872     
7873     getAutoCreate : function()
7874     {
7875         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7876         
7877         cfg = {
7878             tag: 'table',
7879             cls : 'table',
7880             cn : []
7881         };
7882         if (this.scrollBody) {
7883             cfg.cls += ' table-body-fixed';
7884         }    
7885         if (this.striped) {
7886             cfg.cls += ' table-striped';
7887         }
7888         
7889         if (this.hover) {
7890             cfg.cls += ' table-hover';
7891         }
7892         if (this.bordered) {
7893             cfg.cls += ' table-bordered';
7894         }
7895         if (this.condensed) {
7896             cfg.cls += ' table-condensed';
7897         }
7898         if (this.responsive) {
7899             cfg.cls += ' table-responsive';
7900         }
7901         
7902         if (this.cls) {
7903             cfg.cls+=  ' ' +this.cls;
7904         }
7905         
7906         // this lot should be simplifed...
7907         var _t = this;
7908         var cp = [
7909             'align',
7910             'bgcolor',
7911             'border',
7912             'cellpadding',
7913             'cellspacing',
7914             'frame',
7915             'rules',
7916             'sortable',
7917             'summary',
7918             'width'
7919         ].forEach(function(k) {
7920             if (_t[k]) {
7921                 cfg[k] = _t[k];
7922             }
7923         });
7924         
7925         
7926         if (this.layout) {
7927             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7928         }
7929         
7930         if(this.store || this.cm){
7931             if(this.headerShow){
7932                 cfg.cn.push(this.renderHeader());
7933             }
7934             
7935             cfg.cn.push(this.renderBody());
7936             
7937             if(this.footerShow){
7938                 cfg.cn.push(this.renderFooter());
7939             }
7940             // where does this come from?
7941             //cfg.cls+=  ' TableGrid';
7942         }
7943         
7944         return { cn : [ cfg ] };
7945     },
7946     
7947     initEvents : function()
7948     {   
7949         if(!this.store || !this.cm){
7950             return;
7951         }
7952         if (this.selModel) {
7953             this.selModel.initEvents();
7954         }
7955         
7956         
7957         //Roo.log('initEvents with ds!!!!');
7958         
7959         this.mainBody = this.el.select('tbody', true).first();
7960         this.mainHead = this.el.select('thead', true).first();
7961         this.mainFoot = this.el.select('tfoot', true).first();
7962         
7963         
7964         
7965         var _this = this;
7966         
7967         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7968             e.on('click', _this.sort, _this);
7969         });
7970         
7971         this.mainBody.on("click", this.onClick, this);
7972         this.mainBody.on("dblclick", this.onDblClick, this);
7973         
7974         // why is this done????? = it breaks dialogs??
7975         //this.parent().el.setStyle('position', 'relative');
7976         
7977         
7978         if (this.footer) {
7979             this.footer.parentId = this.id;
7980             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7981             
7982             if(this.lazyLoad){
7983                 this.el.select('tfoot tr td').first().addClass('hide');
7984             }
7985         } 
7986         
7987         if(this.loadMask) {
7988             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7989         }
7990         
7991         this.store.on('load', this.onLoad, this);
7992         this.store.on('beforeload', this.onBeforeLoad, this);
7993         this.store.on('update', this.onUpdate, this);
7994         this.store.on('add', this.onAdd, this);
7995         this.store.on("clear", this.clear, this);
7996         
7997         this.el.on("contextmenu", this.onContextMenu, this);
7998         
7999         this.mainBody.on('scroll', this.onBodyScroll, this);
8000         
8001         this.cm.on("headerchange", this.onHeaderChange, this);
8002         
8003         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8004         
8005     },
8006     
8007     onContextMenu : function(e, t)
8008     {
8009         this.processEvent("contextmenu", e);
8010     },
8011     
8012     processEvent : function(name, e)
8013     {
8014         if (name != 'touchstart' ) {
8015             this.fireEvent(name, e);    
8016         }
8017         
8018         var t = e.getTarget();
8019         
8020         var cell = Roo.get(t);
8021         
8022         if(!cell){
8023             return;
8024         }
8025         
8026         if(cell.findParent('tfoot', false, true)){
8027             return;
8028         }
8029         
8030         if(cell.findParent('thead', false, true)){
8031             
8032             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8033                 cell = Roo.get(t).findParent('th', false, true);
8034                 if (!cell) {
8035                     Roo.log("failed to find th in thead?");
8036                     Roo.log(e.getTarget());
8037                     return;
8038                 }
8039             }
8040             
8041             var cellIndex = cell.dom.cellIndex;
8042             
8043             var ename = name == 'touchstart' ? 'click' : name;
8044             this.fireEvent("header" + ename, this, cellIndex, e);
8045             
8046             return;
8047         }
8048         
8049         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8050             cell = Roo.get(t).findParent('td', false, true);
8051             if (!cell) {
8052                 Roo.log("failed to find th in tbody?");
8053                 Roo.log(e.getTarget());
8054                 return;
8055             }
8056         }
8057         
8058         var row = cell.findParent('tr', false, true);
8059         var cellIndex = cell.dom.cellIndex;
8060         var rowIndex = row.dom.rowIndex - 1;
8061         
8062         if(row !== false){
8063             
8064             this.fireEvent("row" + name, this, rowIndex, e);
8065             
8066             if(cell !== false){
8067             
8068                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8069             }
8070         }
8071         
8072     },
8073     
8074     onMouseover : function(e, el)
8075     {
8076         var cell = Roo.get(el);
8077         
8078         if(!cell){
8079             return;
8080         }
8081         
8082         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8083             cell = cell.findParent('td', false, true);
8084         }
8085         
8086         var row = cell.findParent('tr', false, true);
8087         var cellIndex = cell.dom.cellIndex;
8088         var rowIndex = row.dom.rowIndex - 1; // start from 0
8089         
8090         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8091         
8092     },
8093     
8094     onMouseout : function(e, el)
8095     {
8096         var cell = Roo.get(el);
8097         
8098         if(!cell){
8099             return;
8100         }
8101         
8102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8103             cell = cell.findParent('td', false, true);
8104         }
8105         
8106         var row = cell.findParent('tr', false, true);
8107         var cellIndex = cell.dom.cellIndex;
8108         var rowIndex = row.dom.rowIndex - 1; // start from 0
8109         
8110         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8111         
8112     },
8113     
8114     onClick : function(e, el)
8115     {
8116         var cell = Roo.get(el);
8117         
8118         if(!cell || (!this.cellSelection && !this.rowSelection)){
8119             return;
8120         }
8121         
8122         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8123             cell = cell.findParent('td', false, true);
8124         }
8125         
8126         if(!cell || typeof(cell) == 'undefined'){
8127             return;
8128         }
8129         
8130         var row = cell.findParent('tr', false, true);
8131         
8132         if(!row || typeof(row) == 'undefined'){
8133             return;
8134         }
8135         
8136         var cellIndex = cell.dom.cellIndex;
8137         var rowIndex = this.getRowIndex(row);
8138         
8139         // why??? - should these not be based on SelectionModel?
8140         if(this.cellSelection){
8141             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8142         }
8143         
8144         if(this.rowSelection){
8145             this.fireEvent('rowclick', this, row, rowIndex, e);
8146         }
8147         
8148         
8149     },
8150         
8151     onDblClick : function(e,el)
8152     {
8153         var cell = Roo.get(el);
8154         
8155         if(!cell || (!this.cellSelection && !this.rowSelection)){
8156             return;
8157         }
8158         
8159         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160             cell = cell.findParent('td', false, true);
8161         }
8162         
8163         if(!cell || typeof(cell) == 'undefined'){
8164             return;
8165         }
8166         
8167         var row = cell.findParent('tr', false, true);
8168         
8169         if(!row || typeof(row) == 'undefined'){
8170             return;
8171         }
8172         
8173         var cellIndex = cell.dom.cellIndex;
8174         var rowIndex = this.getRowIndex(row);
8175         
8176         if(this.cellSelection){
8177             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8178         }
8179         
8180         if(this.rowSelection){
8181             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8182         }
8183     },
8184     
8185     sort : function(e,el)
8186     {
8187         var col = Roo.get(el);
8188         
8189         if(!col.hasClass('sortable')){
8190             return;
8191         }
8192         
8193         var sort = col.attr('sort');
8194         var dir = 'ASC';
8195         
8196         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8197             dir = 'DESC';
8198         }
8199         
8200         this.store.sortInfo = {field : sort, direction : dir};
8201         
8202         if (this.footer) {
8203             Roo.log("calling footer first");
8204             this.footer.onClick('first');
8205         } else {
8206         
8207             this.store.load({ params : { start : 0 } });
8208         }
8209     },
8210     
8211     renderHeader : function()
8212     {
8213         var header = {
8214             tag: 'thead',
8215             cn : []
8216         };
8217         
8218         var cm = this.cm;
8219         this.totalWidth = 0;
8220         
8221         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8222             
8223             var config = cm.config[i];
8224             
8225             var c = {
8226                 tag: 'th',
8227                 cls : 'x-hcol-' + i,
8228                 style : '',
8229                 html: cm.getColumnHeader(i)
8230             };
8231             
8232             var hh = '';
8233             
8234             if(typeof(config.sortable) != 'undefined' && config.sortable){
8235                 c.cls = 'sortable';
8236                 c.html = '<i class="glyphicon"></i>' + c.html;
8237             }
8238             
8239             // could use BS4 hidden-..-down 
8240             
8241             if(typeof(config.lgHeader) != 'undefined'){
8242                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8243             }
8244             
8245             if(typeof(config.mdHeader) != 'undefined'){
8246                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8247             }
8248             
8249             if(typeof(config.smHeader) != 'undefined'){
8250                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8251             }
8252             
8253             if(typeof(config.xsHeader) != 'undefined'){
8254                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8255             }
8256             
8257             if(hh.length){
8258                 c.html = hh;
8259             }
8260             
8261             if(typeof(config.tooltip) != 'undefined'){
8262                 c.tooltip = config.tooltip;
8263             }
8264             
8265             if(typeof(config.colspan) != 'undefined'){
8266                 c.colspan = config.colspan;
8267             }
8268             
8269             if(typeof(config.hidden) != 'undefined' && config.hidden){
8270                 c.style += ' display:none;';
8271             }
8272             
8273             if(typeof(config.dataIndex) != 'undefined'){
8274                 c.sort = config.dataIndex;
8275             }
8276             
8277            
8278             
8279             if(typeof(config.align) != 'undefined' && config.align.length){
8280                 c.style += ' text-align:' + config.align + ';';
8281             }
8282             
8283             if(typeof(config.width) != 'undefined'){
8284                 c.style += ' width:' + config.width + 'px;';
8285                 this.totalWidth += config.width;
8286             } else {
8287                 this.totalWidth += 100; // assume minimum of 100 per column?
8288             }
8289             
8290             if(typeof(config.cls) != 'undefined'){
8291                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8292             }
8293             
8294             ['xs','sm','md','lg'].map(function(size){
8295                 
8296                 if(typeof(config[size]) == 'undefined'){
8297                     return;
8298                 }
8299                  
8300                 if (!config[size]) { // 0 = hidden
8301                     // BS 4 '0' is treated as hide that column and below.
8302                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8303                     return;
8304                 }
8305                 
8306                 c.cls += ' col-' + size + '-' + config[size] + (
8307                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8308                 );
8309                 
8310                 
8311             });
8312             
8313             header.cn.push(c)
8314         }
8315         
8316         return header;
8317     },
8318     
8319     renderBody : function()
8320     {
8321         var body = {
8322             tag: 'tbody',
8323             cn : [
8324                 {
8325                     tag: 'tr',
8326                     cn : [
8327                         {
8328                             tag : 'td',
8329                             colspan :  this.cm.getColumnCount()
8330                         }
8331                     ]
8332                 }
8333             ]
8334         };
8335         
8336         return body;
8337     },
8338     
8339     renderFooter : function()
8340     {
8341         var footer = {
8342             tag: 'tfoot',
8343             cn : [
8344                 {
8345                     tag: 'tr',
8346                     cn : [
8347                         {
8348                             tag : 'td',
8349                             colspan :  this.cm.getColumnCount()
8350                         }
8351                     ]
8352                 }
8353             ]
8354         };
8355         
8356         return footer;
8357     },
8358     
8359     
8360     
8361     onLoad : function()
8362     {
8363 //        Roo.log('ds onload');
8364         this.clear();
8365         
8366         var _this = this;
8367         var cm = this.cm;
8368         var ds = this.store;
8369         
8370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8371             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8372             if (_this.store.sortInfo) {
8373                     
8374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8375                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8376                 }
8377                 
8378                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8379                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8380                 }
8381             }
8382         });
8383         
8384         var tbody =  this.mainBody;
8385               
8386         if(ds.getCount() > 0){
8387             ds.data.each(function(d,rowIndex){
8388                 var row =  this.renderRow(cm, ds, rowIndex);
8389                 
8390                 tbody.createChild(row);
8391                 
8392                 var _this = this;
8393                 
8394                 if(row.cellObjects.length){
8395                     Roo.each(row.cellObjects, function(r){
8396                         _this.renderCellObject(r);
8397                     })
8398                 }
8399                 
8400             }, this);
8401         }
8402         
8403         var tfoot = this.el.select('tfoot', true).first();
8404         
8405         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8406             
8407             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8408             
8409             var total = this.ds.getTotalCount();
8410             
8411             if(this.footer.pageSize < total){
8412                 this.mainFoot.show();
8413             }
8414         }
8415         
8416         Roo.each(this.el.select('tbody td', true).elements, function(e){
8417             e.on('mouseover', _this.onMouseover, _this);
8418         });
8419         
8420         Roo.each(this.el.select('tbody td', true).elements, function(e){
8421             e.on('mouseout', _this.onMouseout, _this);
8422         });
8423         this.fireEvent('rowsrendered', this);
8424         
8425         this.autoSize();
8426     },
8427     
8428     
8429     onUpdate : function(ds,record)
8430     {
8431         this.refreshRow(record);
8432         this.autoSize();
8433     },
8434     
8435     onRemove : function(ds, record, index, isUpdate){
8436         if(isUpdate !== true){
8437             this.fireEvent("beforerowremoved", this, index, record);
8438         }
8439         var bt = this.mainBody.dom;
8440         
8441         var rows = this.el.select('tbody > tr', true).elements;
8442         
8443         if(typeof(rows[index]) != 'undefined'){
8444             bt.removeChild(rows[index].dom);
8445         }
8446         
8447 //        if(bt.rows[index]){
8448 //            bt.removeChild(bt.rows[index]);
8449 //        }
8450         
8451         if(isUpdate !== true){
8452             //this.stripeRows(index);
8453             //this.syncRowHeights(index, index);
8454             //this.layout();
8455             this.fireEvent("rowremoved", this, index, record);
8456         }
8457     },
8458     
8459     onAdd : function(ds, records, rowIndex)
8460     {
8461         //Roo.log('on Add called');
8462         // - note this does not handle multiple adding very well..
8463         var bt = this.mainBody.dom;
8464         for (var i =0 ; i < records.length;i++) {
8465             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8466             //Roo.log(records[i]);
8467             //Roo.log(this.store.getAt(rowIndex+i));
8468             this.insertRow(this.store, rowIndex + i, false);
8469             return;
8470         }
8471         
8472     },
8473     
8474     
8475     refreshRow : function(record){
8476         var ds = this.store, index;
8477         if(typeof record == 'number'){
8478             index = record;
8479             record = ds.getAt(index);
8480         }else{
8481             index = ds.indexOf(record);
8482             if (index < 0) {
8483                 return; // should not happen - but seems to 
8484             }
8485         }
8486         this.insertRow(ds, index, true);
8487         this.autoSize();
8488         this.onRemove(ds, record, index+1, true);
8489         this.autoSize();
8490         //this.syncRowHeights(index, index);
8491         //this.layout();
8492         this.fireEvent("rowupdated", this, index, record);
8493     },
8494     
8495     insertRow : function(dm, rowIndex, isUpdate){
8496         
8497         if(!isUpdate){
8498             this.fireEvent("beforerowsinserted", this, rowIndex);
8499         }
8500             //var s = this.getScrollState();
8501         var row = this.renderRow(this.cm, this.store, rowIndex);
8502         // insert before rowIndex..
8503         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8504         
8505         var _this = this;
8506                 
8507         if(row.cellObjects.length){
8508             Roo.each(row.cellObjects, function(r){
8509                 _this.renderCellObject(r);
8510             })
8511         }
8512             
8513         if(!isUpdate){
8514             this.fireEvent("rowsinserted", this, rowIndex);
8515             //this.syncRowHeights(firstRow, lastRow);
8516             //this.stripeRows(firstRow);
8517             //this.layout();
8518         }
8519         
8520     },
8521     
8522     
8523     getRowDom : function(rowIndex)
8524     {
8525         var rows = this.el.select('tbody > tr', true).elements;
8526         
8527         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8528         
8529     },
8530     // returns the object tree for a tr..
8531   
8532     
8533     renderRow : function(cm, ds, rowIndex) 
8534     {
8535         var d = ds.getAt(rowIndex);
8536         
8537         var row = {
8538             tag : 'tr',
8539             cls : 'x-row-' + rowIndex,
8540             cn : []
8541         };
8542             
8543         var cellObjects = [];
8544         
8545         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8546             var config = cm.config[i];
8547             
8548             var renderer = cm.getRenderer(i);
8549             var value = '';
8550             var id = false;
8551             
8552             if(typeof(renderer) !== 'undefined'){
8553                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8554             }
8555             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8556             // and are rendered into the cells after the row is rendered - using the id for the element.
8557             
8558             if(typeof(value) === 'object'){
8559                 id = Roo.id();
8560                 cellObjects.push({
8561                     container : id,
8562                     cfg : value 
8563                 })
8564             }
8565             
8566             var rowcfg = {
8567                 record: d,
8568                 rowIndex : rowIndex,
8569                 colIndex : i,
8570                 rowClass : ''
8571             };
8572
8573             this.fireEvent('rowclass', this, rowcfg);
8574             
8575             var td = {
8576                 tag: 'td',
8577                 cls : rowcfg.rowClass + ' x-col-' + i,
8578                 style: '',
8579                 html: (typeof(value) === 'object') ? '' : value
8580             };
8581             
8582             if (id) {
8583                 td.id = id;
8584             }
8585             
8586             if(typeof(config.colspan) != 'undefined'){
8587                 td.colspan = config.colspan;
8588             }
8589             
8590             if(typeof(config.hidden) != 'undefined' && config.hidden){
8591                 td.style += ' display:none;';
8592             }
8593             
8594             if(typeof(config.align) != 'undefined' && config.align.length){
8595                 td.style += ' text-align:' + config.align + ';';
8596             }
8597             if(typeof(config.valign) != 'undefined' && config.valign.length){
8598                 td.style += ' vertical-align:' + config.valign + ';';
8599             }
8600             
8601             if(typeof(config.width) != 'undefined'){
8602                 td.style += ' width:' +  config.width + 'px;';
8603             }
8604             
8605             if(typeof(config.cursor) != 'undefined'){
8606                 td.style += ' cursor:' +  config.cursor + ';';
8607             }
8608             
8609             if(typeof(config.cls) != 'undefined'){
8610                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8611             }
8612             
8613             ['xs','sm','md','lg'].map(function(size){
8614                 
8615                 if(typeof(config[size]) == 'undefined'){
8616                     return;
8617                 }
8618                 
8619                 
8620                   
8621                 if (!config[size]) { // 0 = hidden
8622                     // BS 4 '0' is treated as hide that column and below.
8623                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8624                     return;
8625                 }
8626                 
8627                 td.cls += ' col-' + size + '-' + config[size] + (
8628                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8629                 );
8630                  
8631
8632             });
8633             
8634             row.cn.push(td);
8635            
8636         }
8637         
8638         row.cellObjects = cellObjects;
8639         
8640         return row;
8641           
8642     },
8643     
8644     
8645     
8646     onBeforeLoad : function()
8647     {
8648         
8649     },
8650      /**
8651      * Remove all rows
8652      */
8653     clear : function()
8654     {
8655         this.el.select('tbody', true).first().dom.innerHTML = '';
8656     },
8657     /**
8658      * Show or hide a row.
8659      * @param {Number} rowIndex to show or hide
8660      * @param {Boolean} state hide
8661      */
8662     setRowVisibility : function(rowIndex, state)
8663     {
8664         var bt = this.mainBody.dom;
8665         
8666         var rows = this.el.select('tbody > tr', true).elements;
8667         
8668         if(typeof(rows[rowIndex]) == 'undefined'){
8669             return;
8670         }
8671         rows[rowIndex].dom.style.display = state ? '' : 'none';
8672     },
8673     
8674     
8675     getSelectionModel : function(){
8676         if(!this.selModel){
8677             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8678         }
8679         return this.selModel;
8680     },
8681     /*
8682      * Render the Roo.bootstrap object from renderder
8683      */
8684     renderCellObject : function(r)
8685     {
8686         var _this = this;
8687         
8688         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8689         
8690         var t = r.cfg.render(r.container);
8691         
8692         if(r.cfg.cn){
8693             Roo.each(r.cfg.cn, function(c){
8694                 var child = {
8695                     container: t.getChildContainer(),
8696                     cfg: c
8697                 };
8698                 _this.renderCellObject(child);
8699             })
8700         }
8701     },
8702     
8703     getRowIndex : function(row)
8704     {
8705         var rowIndex = -1;
8706         
8707         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8708             if(el != row){
8709                 return;
8710             }
8711             
8712             rowIndex = index;
8713         });
8714         
8715         return rowIndex;
8716     },
8717      /**
8718      * Returns the grid's underlying element = used by panel.Grid
8719      * @return {Element} The element
8720      */
8721     getGridEl : function(){
8722         return this.el;
8723     },
8724      /**
8725      * Forces a resize - used by panel.Grid
8726      * @return {Element} The element
8727      */
8728     autoSize : function()
8729     {
8730         //var ctr = Roo.get(this.container.dom.parentElement);
8731         var ctr = Roo.get(this.el.dom);
8732         
8733         var thd = this.getGridEl().select('thead',true).first();
8734         var tbd = this.getGridEl().select('tbody', true).first();
8735         var tfd = this.getGridEl().select('tfoot', true).first();
8736         
8737         var cw = ctr.getWidth();
8738         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8739         
8740         if (tbd) {
8741             
8742             tbd.setWidth(ctr.getWidth());
8743             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8744             // this needs fixing for various usage - currently only hydra job advers I think..
8745             //tdb.setHeight(
8746             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8747             //); 
8748             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8749             cw -= barsize;
8750         }
8751         cw = Math.max(cw, this.totalWidth);
8752         this.getGridEl().select('tbody tr',true).setWidth(cw);
8753         
8754         // resize 'expandable coloumn?
8755         
8756         return; // we doe not have a view in this design..
8757         
8758     },
8759     onBodyScroll: function()
8760     {
8761         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8762         if(this.mainHead){
8763             this.mainHead.setStyle({
8764                 'position' : 'relative',
8765                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8766             });
8767         }
8768         
8769         if(this.lazyLoad){
8770             
8771             var scrollHeight = this.mainBody.dom.scrollHeight;
8772             
8773             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8774             
8775             var height = this.mainBody.getHeight();
8776             
8777             if(scrollHeight - height == scrollTop) {
8778                 
8779                 var total = this.ds.getTotalCount();
8780                 
8781                 if(this.footer.cursor + this.footer.pageSize < total){
8782                     
8783                     this.footer.ds.load({
8784                         params : {
8785                             start : this.footer.cursor + this.footer.pageSize,
8786                             limit : this.footer.pageSize
8787                         },
8788                         add : true
8789                     });
8790                 }
8791             }
8792             
8793         }
8794     },
8795     
8796     onHeaderChange : function()
8797     {
8798         var header = this.renderHeader();
8799         var table = this.el.select('table', true).first();
8800         
8801         this.mainHead.remove();
8802         this.mainHead = table.createChild(header, this.mainBody, false);
8803     },
8804     
8805     onHiddenChange : function(colModel, colIndex, hidden)
8806     {
8807         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8808         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8809         
8810         this.CSS.updateRule(thSelector, "display", "");
8811         this.CSS.updateRule(tdSelector, "display", "");
8812         
8813         if(hidden){
8814             this.CSS.updateRule(thSelector, "display", "none");
8815             this.CSS.updateRule(tdSelector, "display", "none");
8816         }
8817         
8818         this.onHeaderChange();
8819         this.onLoad();
8820     },
8821     
8822     setColumnWidth: function(col_index, width)
8823     {
8824         // width = "md-2 xs-2..."
8825         if(!this.colModel.config[col_index]) {
8826             return;
8827         }
8828         
8829         var w = width.split(" ");
8830         
8831         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8832         
8833         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8834         
8835         
8836         for(var j = 0; j < w.length; j++) {
8837             
8838             if(!w[j]) {
8839                 continue;
8840             }
8841             
8842             var size_cls = w[j].split("-");
8843             
8844             if(!Number.isInteger(size_cls[1] * 1)) {
8845                 continue;
8846             }
8847             
8848             if(!this.colModel.config[col_index][size_cls[0]]) {
8849                 continue;
8850             }
8851             
8852             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8853                 continue;
8854             }
8855             
8856             h_row[0].classList.replace(
8857                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8858                 "col-"+size_cls[0]+"-"+size_cls[1]
8859             );
8860             
8861             for(var i = 0; i < rows.length; i++) {
8862                 
8863                 var size_cls = w[j].split("-");
8864                 
8865                 if(!Number.isInteger(size_cls[1] * 1)) {
8866                     continue;
8867                 }
8868                 
8869                 if(!this.colModel.config[col_index][size_cls[0]]) {
8870                     continue;
8871                 }
8872                 
8873                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8874                     continue;
8875                 }
8876                 
8877                 rows[i].classList.replace(
8878                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8879                     "col-"+size_cls[0]+"-"+size_cls[1]
8880                 );
8881             }
8882             
8883             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8884         }
8885     }
8886 });
8887
8888  
8889
8890  /*
8891  * - LGPL
8892  *
8893  * table cell
8894  * 
8895  */
8896
8897 /**
8898  * @class Roo.bootstrap.TableCell
8899  * @extends Roo.bootstrap.Component
8900  * Bootstrap TableCell class
8901  * @cfg {String} html cell contain text
8902  * @cfg {String} cls cell class
8903  * @cfg {String} tag cell tag (td|th) default td
8904  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8905  * @cfg {String} align Aligns the content in a cell
8906  * @cfg {String} axis Categorizes cells
8907  * @cfg {String} bgcolor Specifies the background color of a cell
8908  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8909  * @cfg {Number} colspan Specifies the number of columns a cell should span
8910  * @cfg {String} headers Specifies one or more header cells a cell is related to
8911  * @cfg {Number} height Sets the height of a cell
8912  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8913  * @cfg {Number} rowspan Sets the number of rows a cell should span
8914  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8915  * @cfg {String} valign Vertical aligns the content in a cell
8916  * @cfg {Number} width Specifies the width of a cell
8917  * 
8918  * @constructor
8919  * Create a new TableCell
8920  * @param {Object} config The config object
8921  */
8922
8923 Roo.bootstrap.TableCell = function(config){
8924     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8925 };
8926
8927 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8928     
8929     html: false,
8930     cls: false,
8931     tag: false,
8932     abbr: false,
8933     align: false,
8934     axis: false,
8935     bgcolor: false,
8936     charoff: false,
8937     colspan: false,
8938     headers: false,
8939     height: false,
8940     nowrap: false,
8941     rowspan: false,
8942     scope: false,
8943     valign: false,
8944     width: false,
8945     
8946     
8947     getAutoCreate : function(){
8948         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8949         
8950         cfg = {
8951             tag: 'td'
8952         };
8953         
8954         if(this.tag){
8955             cfg.tag = this.tag;
8956         }
8957         
8958         if (this.html) {
8959             cfg.html=this.html
8960         }
8961         if (this.cls) {
8962             cfg.cls=this.cls
8963         }
8964         if (this.abbr) {
8965             cfg.abbr=this.abbr
8966         }
8967         if (this.align) {
8968             cfg.align=this.align
8969         }
8970         if (this.axis) {
8971             cfg.axis=this.axis
8972         }
8973         if (this.bgcolor) {
8974             cfg.bgcolor=this.bgcolor
8975         }
8976         if (this.charoff) {
8977             cfg.charoff=this.charoff
8978         }
8979         if (this.colspan) {
8980             cfg.colspan=this.colspan
8981         }
8982         if (this.headers) {
8983             cfg.headers=this.headers
8984         }
8985         if (this.height) {
8986             cfg.height=this.height
8987         }
8988         if (this.nowrap) {
8989             cfg.nowrap=this.nowrap
8990         }
8991         if (this.rowspan) {
8992             cfg.rowspan=this.rowspan
8993         }
8994         if (this.scope) {
8995             cfg.scope=this.scope
8996         }
8997         if (this.valign) {
8998             cfg.valign=this.valign
8999         }
9000         if (this.width) {
9001             cfg.width=this.width
9002         }
9003         
9004         
9005         return cfg;
9006     }
9007    
9008 });
9009
9010  
9011
9012  /*
9013  * - LGPL
9014  *
9015  * table row
9016  * 
9017  */
9018
9019 /**
9020  * @class Roo.bootstrap.TableRow
9021  * @extends Roo.bootstrap.Component
9022  * Bootstrap TableRow class
9023  * @cfg {String} cls row class
9024  * @cfg {String} align Aligns the content in a table row
9025  * @cfg {String} bgcolor Specifies a background color for a table row
9026  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9027  * @cfg {String} valign Vertical aligns the content in a table row
9028  * 
9029  * @constructor
9030  * Create a new TableRow
9031  * @param {Object} config The config object
9032  */
9033
9034 Roo.bootstrap.TableRow = function(config){
9035     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9036 };
9037
9038 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9039     
9040     cls: false,
9041     align: false,
9042     bgcolor: false,
9043     charoff: false,
9044     valign: false,
9045     
9046     getAutoCreate : function(){
9047         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9048         
9049         cfg = {
9050             tag: 'tr'
9051         };
9052             
9053         if(this.cls){
9054             cfg.cls = this.cls;
9055         }
9056         if(this.align){
9057             cfg.align = this.align;
9058         }
9059         if(this.bgcolor){
9060             cfg.bgcolor = this.bgcolor;
9061         }
9062         if(this.charoff){
9063             cfg.charoff = this.charoff;
9064         }
9065         if(this.valign){
9066             cfg.valign = this.valign;
9067         }
9068         
9069         return cfg;
9070     }
9071    
9072 });
9073
9074  
9075
9076  /*
9077  * - LGPL
9078  *
9079  * table body
9080  * 
9081  */
9082
9083 /**
9084  * @class Roo.bootstrap.TableBody
9085  * @extends Roo.bootstrap.Component
9086  * Bootstrap TableBody class
9087  * @cfg {String} cls element class
9088  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9089  * @cfg {String} align Aligns the content inside the element
9090  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9091  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9092  * 
9093  * @constructor
9094  * Create a new TableBody
9095  * @param {Object} config The config object
9096  */
9097
9098 Roo.bootstrap.TableBody = function(config){
9099     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9100 };
9101
9102 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9103     
9104     cls: false,
9105     tag: false,
9106     align: false,
9107     charoff: false,
9108     valign: false,
9109     
9110     getAutoCreate : function(){
9111         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9112         
9113         cfg = {
9114             tag: 'tbody'
9115         };
9116             
9117         if (this.cls) {
9118             cfg.cls=this.cls
9119         }
9120         if(this.tag){
9121             cfg.tag = this.tag;
9122         }
9123         
9124         if(this.align){
9125             cfg.align = this.align;
9126         }
9127         if(this.charoff){
9128             cfg.charoff = this.charoff;
9129         }
9130         if(this.valign){
9131             cfg.valign = this.valign;
9132         }
9133         
9134         return cfg;
9135     }
9136     
9137     
9138 //    initEvents : function()
9139 //    {
9140 //        
9141 //        if(!this.store){
9142 //            return;
9143 //        }
9144 //        
9145 //        this.store = Roo.factory(this.store, Roo.data);
9146 //        this.store.on('load', this.onLoad, this);
9147 //        
9148 //        this.store.load();
9149 //        
9150 //    },
9151 //    
9152 //    onLoad: function () 
9153 //    {   
9154 //        this.fireEvent('load', this);
9155 //    }
9156 //    
9157 //   
9158 });
9159
9160  
9161
9162  /*
9163  * Based on:
9164  * Ext JS Library 1.1.1
9165  * Copyright(c) 2006-2007, Ext JS, LLC.
9166  *
9167  * Originally Released Under LGPL - original licence link has changed is not relivant.
9168  *
9169  * Fork - LGPL
9170  * <script type="text/javascript">
9171  */
9172
9173 // as we use this in bootstrap.
9174 Roo.namespace('Roo.form');
9175  /**
9176  * @class Roo.form.Action
9177  * Internal Class used to handle form actions
9178  * @constructor
9179  * @param {Roo.form.BasicForm} el The form element or its id
9180  * @param {Object} config Configuration options
9181  */
9182
9183  
9184  
9185 // define the action interface
9186 Roo.form.Action = function(form, options){
9187     this.form = form;
9188     this.options = options || {};
9189 };
9190 /**
9191  * Client Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.CLIENT_INVALID = 'client';
9195 /**
9196  * Server Validation Failed
9197  * @const 
9198  */
9199 Roo.form.Action.SERVER_INVALID = 'server';
9200  /**
9201  * Connect to Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.CONNECT_FAILURE = 'connect';
9205 /**
9206  * Reading Data from Server Failed
9207  * @const 
9208  */
9209 Roo.form.Action.LOAD_FAILURE = 'load';
9210
9211 Roo.form.Action.prototype = {
9212     type : 'default',
9213     failureType : undefined,
9214     response : undefined,
9215     result : undefined,
9216
9217     // interface method
9218     run : function(options){
9219
9220     },
9221
9222     // interface method
9223     success : function(response){
9224
9225     },
9226
9227     // interface method
9228     handleResponse : function(response){
9229
9230     },
9231
9232     // default connection failure
9233     failure : function(response){
9234         
9235         this.response = response;
9236         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9237         this.form.afterAction(this, false);
9238     },
9239
9240     processResponse : function(response){
9241         this.response = response;
9242         if(!response.responseText){
9243             return true;
9244         }
9245         this.result = this.handleResponse(response);
9246         return this.result;
9247     },
9248
9249     // utility functions used internally
9250     getUrl : function(appendParams){
9251         var url = this.options.url || this.form.url || this.form.el.dom.action;
9252         if(appendParams){
9253             var p = this.getParams();
9254             if(p){
9255                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9256             }
9257         }
9258         return url;
9259     },
9260
9261     getMethod : function(){
9262         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9263     },
9264
9265     getParams : function(){
9266         var bp = this.form.baseParams;
9267         var p = this.options.params;
9268         if(p){
9269             if(typeof p == "object"){
9270                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9271             }else if(typeof p == 'string' && bp){
9272                 p += '&' + Roo.urlEncode(bp);
9273             }
9274         }else if(bp){
9275             p = Roo.urlEncode(bp);
9276         }
9277         return p;
9278     },
9279
9280     createCallback : function(){
9281         return {
9282             success: this.success,
9283             failure: this.failure,
9284             scope: this,
9285             timeout: (this.form.timeout*1000),
9286             upload: this.form.fileUpload ? this.success : undefined
9287         };
9288     }
9289 };
9290
9291 Roo.form.Action.Submit = function(form, options){
9292     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9293 };
9294
9295 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9296     type : 'submit',
9297
9298     haveProgress : false,
9299     uploadComplete : false,
9300     
9301     // uploadProgress indicator.
9302     uploadProgress : function()
9303     {
9304         if (!this.form.progressUrl) {
9305             return;
9306         }
9307         
9308         if (!this.haveProgress) {
9309             Roo.MessageBox.progress("Uploading", "Uploading");
9310         }
9311         if (this.uploadComplete) {
9312            Roo.MessageBox.hide();
9313            return;
9314         }
9315         
9316         this.haveProgress = true;
9317    
9318         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9319         
9320         var c = new Roo.data.Connection();
9321         c.request({
9322             url : this.form.progressUrl,
9323             params: {
9324                 id : uid
9325             },
9326             method: 'GET',
9327             success : function(req){
9328                //console.log(data);
9329                 var rdata = false;
9330                 var edata;
9331                 try  {
9332                    rdata = Roo.decode(req.responseText)
9333                 } catch (e) {
9334                     Roo.log("Invalid data from server..");
9335                     Roo.log(edata);
9336                     return;
9337                 }
9338                 if (!rdata || !rdata.success) {
9339                     Roo.log(rdata);
9340                     Roo.MessageBox.alert(Roo.encode(rdata));
9341                     return;
9342                 }
9343                 var data = rdata.data;
9344                 
9345                 if (this.uploadComplete) {
9346                    Roo.MessageBox.hide();
9347                    return;
9348                 }
9349                    
9350                 if (data){
9351                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9352                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9353                     );
9354                 }
9355                 this.uploadProgress.defer(2000,this);
9356             },
9357        
9358             failure: function(data) {
9359                 Roo.log('progress url failed ');
9360                 Roo.log(data);
9361             },
9362             scope : this
9363         });
9364            
9365     },
9366     
9367     
9368     run : function()
9369     {
9370         // run get Values on the form, so it syncs any secondary forms.
9371         this.form.getValues();
9372         
9373         var o = this.options;
9374         var method = this.getMethod();
9375         var isPost = method == 'POST';
9376         if(o.clientValidation === false || this.form.isValid()){
9377             
9378             if (this.form.progressUrl) {
9379                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9380                     (new Date() * 1) + '' + Math.random());
9381                     
9382             } 
9383             
9384             
9385             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9386                 form:this.form.el.dom,
9387                 url:this.getUrl(!isPost),
9388                 method: method,
9389                 params:isPost ? this.getParams() : null,
9390                 isUpload: this.form.fileUpload,
9391                 formData : this.form.formData
9392             }));
9393             
9394             this.uploadProgress();
9395
9396         }else if (o.clientValidation !== false){ // client validation failed
9397             this.failureType = Roo.form.Action.CLIENT_INVALID;
9398             this.form.afterAction(this, false);
9399         }
9400     },
9401
9402     success : function(response)
9403     {
9404         this.uploadComplete= true;
9405         if (this.haveProgress) {
9406             Roo.MessageBox.hide();
9407         }
9408         
9409         
9410         var result = this.processResponse(response);
9411         if(result === true || result.success){
9412             this.form.afterAction(this, true);
9413             return;
9414         }
9415         if(result.errors){
9416             this.form.markInvalid(result.errors);
9417             this.failureType = Roo.form.Action.SERVER_INVALID;
9418         }
9419         this.form.afterAction(this, false);
9420     },
9421     failure : function(response)
9422     {
9423         this.uploadComplete= true;
9424         if (this.haveProgress) {
9425             Roo.MessageBox.hide();
9426         }
9427         
9428         this.response = response;
9429         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9430         this.form.afterAction(this, false);
9431     },
9432     
9433     handleResponse : function(response){
9434         if(this.form.errorReader){
9435             var rs = this.form.errorReader.read(response);
9436             var errors = [];
9437             if(rs.records){
9438                 for(var i = 0, len = rs.records.length; i < len; i++) {
9439                     var r = rs.records[i];
9440                     errors[i] = r.data;
9441                 }
9442             }
9443             if(errors.length < 1){
9444                 errors = null;
9445             }
9446             return {
9447                 success : rs.success,
9448                 errors : errors
9449             };
9450         }
9451         var ret = false;
9452         try {
9453             ret = Roo.decode(response.responseText);
9454         } catch (e) {
9455             ret = {
9456                 success: false,
9457                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9458                 errors : []
9459             };
9460         }
9461         return ret;
9462         
9463     }
9464 });
9465
9466
9467 Roo.form.Action.Load = function(form, options){
9468     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9469     this.reader = this.form.reader;
9470 };
9471
9472 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9473     type : 'load',
9474
9475     run : function(){
9476         
9477         Roo.Ajax.request(Roo.apply(
9478                 this.createCallback(), {
9479                     method:this.getMethod(),
9480                     url:this.getUrl(false),
9481                     params:this.getParams()
9482         }));
9483     },
9484
9485     success : function(response){
9486         
9487         var result = this.processResponse(response);
9488         if(result === true || !result.success || !result.data){
9489             this.failureType = Roo.form.Action.LOAD_FAILURE;
9490             this.form.afterAction(this, false);
9491             return;
9492         }
9493         this.form.clearInvalid();
9494         this.form.setValues(result.data);
9495         this.form.afterAction(this, true);
9496     },
9497
9498     handleResponse : function(response){
9499         if(this.form.reader){
9500             var rs = this.form.reader.read(response);
9501             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9502             return {
9503                 success : rs.success,
9504                 data : data
9505             };
9506         }
9507         return Roo.decode(response.responseText);
9508     }
9509 });
9510
9511 Roo.form.Action.ACTION_TYPES = {
9512     'load' : Roo.form.Action.Load,
9513     'submit' : Roo.form.Action.Submit
9514 };/*
9515  * - LGPL
9516  *
9517  * form
9518  *
9519  */
9520
9521 /**
9522  * @class Roo.bootstrap.Form
9523  * @extends Roo.bootstrap.Component
9524  * Bootstrap Form class
9525  * @cfg {String} method  GET | POST (default POST)
9526  * @cfg {String} labelAlign top | left (default top)
9527  * @cfg {String} align left  | right - for navbars
9528  * @cfg {Boolean} loadMask load mask when submit (default true)
9529
9530  *
9531  * @constructor
9532  * Create a new Form
9533  * @param {Object} config The config object
9534  */
9535
9536
9537 Roo.bootstrap.Form = function(config){
9538     
9539     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9540     
9541     Roo.bootstrap.Form.popover.apply();
9542     
9543     this.addEvents({
9544         /**
9545          * @event clientvalidation
9546          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9547          * @param {Form} this
9548          * @param {Boolean} valid true if the form has passed client-side validation
9549          */
9550         clientvalidation: true,
9551         /**
9552          * @event beforeaction
9553          * Fires before any action is performed. Return false to cancel the action.
9554          * @param {Form} this
9555          * @param {Action} action The action to be performed
9556          */
9557         beforeaction: true,
9558         /**
9559          * @event actionfailed
9560          * Fires when an action fails.
9561          * @param {Form} this
9562          * @param {Action} action The action that failed
9563          */
9564         actionfailed : true,
9565         /**
9566          * @event actioncomplete
9567          * Fires when an action is completed.
9568          * @param {Form} this
9569          * @param {Action} action The action that completed
9570          */
9571         actioncomplete : true
9572     });
9573 };
9574
9575 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9576
9577      /**
9578      * @cfg {String} method
9579      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9580      */
9581     method : 'POST',
9582     /**
9583      * @cfg {String} url
9584      * The URL to use for form actions if one isn't supplied in the action options.
9585      */
9586     /**
9587      * @cfg {Boolean} fileUpload
9588      * Set to true if this form is a file upload.
9589      */
9590
9591     /**
9592      * @cfg {Object} baseParams
9593      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9594      */
9595
9596     /**
9597      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9598      */
9599     timeout: 30,
9600     /**
9601      * @cfg {Sting} align (left|right) for navbar forms
9602      */
9603     align : 'left',
9604
9605     // private
9606     activeAction : null,
9607
9608     /**
9609      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9610      * element by passing it or its id or mask the form itself by passing in true.
9611      * @type Mixed
9612      */
9613     waitMsgTarget : false,
9614
9615     loadMask : true,
9616     
9617     /**
9618      * @cfg {Boolean} errorMask (true|false) default false
9619      */
9620     errorMask : false,
9621     
9622     /**
9623      * @cfg {Number} maskOffset Default 100
9624      */
9625     maskOffset : 100,
9626     
9627     /**
9628      * @cfg {Boolean} maskBody
9629      */
9630     maskBody : false,
9631
9632     getAutoCreate : function(){
9633
9634         var cfg = {
9635             tag: 'form',
9636             method : this.method || 'POST',
9637             id : this.id || Roo.id(),
9638             cls : ''
9639         };
9640         if (this.parent().xtype.match(/^Nav/)) {
9641             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9642
9643         }
9644
9645         if (this.labelAlign == 'left' ) {
9646             cfg.cls += ' form-horizontal';
9647         }
9648
9649
9650         return cfg;
9651     },
9652     initEvents : function()
9653     {
9654         this.el.on('submit', this.onSubmit, this);
9655         // this was added as random key presses on the form where triggering form submit.
9656         this.el.on('keypress', function(e) {
9657             if (e.getCharCode() != 13) {
9658                 return true;
9659             }
9660             // we might need to allow it for textareas.. and some other items.
9661             // check e.getTarget().
9662
9663             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9664                 return true;
9665             }
9666
9667             Roo.log("keypress blocked");
9668
9669             e.preventDefault();
9670             return false;
9671         });
9672         
9673     },
9674     // private
9675     onSubmit : function(e){
9676         e.stopEvent();
9677     },
9678
9679      /**
9680      * Returns true if client-side validation on the form is successful.
9681      * @return Boolean
9682      */
9683     isValid : function(){
9684         var items = this.getItems();
9685         var valid = true;
9686         var target = false;
9687         
9688         items.each(function(f){
9689             
9690             if(f.validate()){
9691                 return;
9692             }
9693             
9694             Roo.log('invalid field: ' + f.name);
9695             
9696             valid = false;
9697
9698             if(!target && f.el.isVisible(true)){
9699                 target = f;
9700             }
9701            
9702         });
9703         
9704         if(this.errorMask && !valid){
9705             Roo.bootstrap.Form.popover.mask(this, target);
9706         }
9707         
9708         return valid;
9709     },
9710     
9711     /**
9712      * Returns true if any fields in this form have changed since their original load.
9713      * @return Boolean
9714      */
9715     isDirty : function(){
9716         var dirty = false;
9717         var items = this.getItems();
9718         items.each(function(f){
9719            if(f.isDirty()){
9720                dirty = true;
9721                return false;
9722            }
9723            return true;
9724         });
9725         return dirty;
9726     },
9727      /**
9728      * Performs a predefined action (submit or load) or custom actions you define on this form.
9729      * @param {String} actionName The name of the action type
9730      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9731      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9732      * accept other config options):
9733      * <pre>
9734 Property          Type             Description
9735 ----------------  ---------------  ----------------------------------------------------------------------------------
9736 url               String           The url for the action (defaults to the form's url)
9737 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9738 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9739 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9740                                    validate the form on the client (defaults to false)
9741      * </pre>
9742      * @return {BasicForm} this
9743      */
9744     doAction : function(action, options){
9745         if(typeof action == 'string'){
9746             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9747         }
9748         if(this.fireEvent('beforeaction', this, action) !== false){
9749             this.beforeAction(action);
9750             action.run.defer(100, action);
9751         }
9752         return this;
9753     },
9754
9755     // private
9756     beforeAction : function(action){
9757         var o = action.options;
9758         
9759         if(this.loadMask){
9760             
9761             if(this.maskBody){
9762                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9763             } else {
9764                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9765             }
9766         }
9767         // not really supported yet.. ??
9768
9769         //if(this.waitMsgTarget === true){
9770         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771         //}else if(this.waitMsgTarget){
9772         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9773         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else {
9775         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9776        // }
9777
9778     },
9779
9780     // private
9781     afterAction : function(action, success){
9782         this.activeAction = null;
9783         var o = action.options;
9784
9785         if(this.loadMask){
9786             
9787             if(this.maskBody){
9788                 Roo.get(document.body).unmask();
9789             } else {
9790                 this.el.unmask();
9791             }
9792         }
9793         
9794         //if(this.waitMsgTarget === true){
9795 //            this.el.unmask();
9796         //}else if(this.waitMsgTarget){
9797         //    this.waitMsgTarget.unmask();
9798         //}else{
9799         //    Roo.MessageBox.updateProgress(1);
9800         //    Roo.MessageBox.hide();
9801        // }
9802         //
9803         if(success){
9804             if(o.reset){
9805                 this.reset();
9806             }
9807             Roo.callback(o.success, o.scope, [this, action]);
9808             this.fireEvent('actioncomplete', this, action);
9809
9810         }else{
9811
9812             // failure condition..
9813             // we have a scenario where updates need confirming.
9814             // eg. if a locking scenario exists..
9815             // we look for { errors : { needs_confirm : true }} in the response.
9816             if (
9817                 (typeof(action.result) != 'undefined')  &&
9818                 (typeof(action.result.errors) != 'undefined')  &&
9819                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9820            ){
9821                 var _t = this;
9822                 Roo.log("not supported yet");
9823                  /*
9824
9825                 Roo.MessageBox.confirm(
9826                     "Change requires confirmation",
9827                     action.result.errorMsg,
9828                     function(r) {
9829                         if (r != 'yes') {
9830                             return;
9831                         }
9832                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9833                     }
9834
9835                 );
9836                 */
9837
9838
9839                 return;
9840             }
9841
9842             Roo.callback(o.failure, o.scope, [this, action]);
9843             // show an error message if no failed handler is set..
9844             if (!this.hasListener('actionfailed')) {
9845                 Roo.log("need to add dialog support");
9846                 /*
9847                 Roo.MessageBox.alert("Error",
9848                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9849                         action.result.errorMsg :
9850                         "Saving Failed, please check your entries or try again"
9851                 );
9852                 */
9853             }
9854
9855             this.fireEvent('actionfailed', this, action);
9856         }
9857
9858     },
9859     /**
9860      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9861      * @param {String} id The value to search for
9862      * @return Field
9863      */
9864     findField : function(id){
9865         var items = this.getItems();
9866         var field = items.get(id);
9867         if(!field){
9868              items.each(function(f){
9869                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9870                     field = f;
9871                     return false;
9872                 }
9873                 return true;
9874             });
9875         }
9876         return field || null;
9877     },
9878      /**
9879      * Mark fields in this form invalid in bulk.
9880      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9881      * @return {BasicForm} this
9882      */
9883     markInvalid : function(errors){
9884         if(errors instanceof Array){
9885             for(var i = 0, len = errors.length; i < len; i++){
9886                 var fieldError = errors[i];
9887                 var f = this.findField(fieldError.id);
9888                 if(f){
9889                     f.markInvalid(fieldError.msg);
9890                 }
9891             }
9892         }else{
9893             var field, id;
9894             for(id in errors){
9895                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9896                     field.markInvalid(errors[id]);
9897                 }
9898             }
9899         }
9900         //Roo.each(this.childForms || [], function (f) {
9901         //    f.markInvalid(errors);
9902         //});
9903
9904         return this;
9905     },
9906
9907     /**
9908      * Set values for fields in this form in bulk.
9909      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9910      * @return {BasicForm} this
9911      */
9912     setValues : function(values){
9913         if(values instanceof Array){ // array of objects
9914             for(var i = 0, len = values.length; i < len; i++){
9915                 var v = values[i];
9916                 var f = this.findField(v.id);
9917                 if(f){
9918                     f.setValue(v.value);
9919                     if(this.trackResetOnLoad){
9920                         f.originalValue = f.getValue();
9921                     }
9922                 }
9923             }
9924         }else{ // object hash
9925             var field, id;
9926             for(id in values){
9927                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9928
9929                     if (field.setFromData &&
9930                         field.valueField &&
9931                         field.displayField &&
9932                         // combos' with local stores can
9933                         // be queried via setValue()
9934                         // to set their value..
9935                         (field.store && !field.store.isLocal)
9936                         ) {
9937                         // it's a combo
9938                         var sd = { };
9939                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9940                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9941                         field.setFromData(sd);
9942
9943                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9944                         
9945                         field.setFromData(values);
9946                         
9947                     } else {
9948                         field.setValue(values[id]);
9949                     }
9950
9951
9952                     if(this.trackResetOnLoad){
9953                         field.originalValue = field.getValue();
9954                     }
9955                 }
9956             }
9957         }
9958
9959         //Roo.each(this.childForms || [], function (f) {
9960         //    f.setValues(values);
9961         //});
9962
9963         return this;
9964     },
9965
9966     /**
9967      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9968      * they are returned as an array.
9969      * @param {Boolean} asString
9970      * @return {Object}
9971      */
9972     getValues : function(asString){
9973         //if (this.childForms) {
9974             // copy values from the child forms
9975         //    Roo.each(this.childForms, function (f) {
9976         //        this.setValues(f.getValues());
9977         //    }, this);
9978         //}
9979
9980
9981
9982         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9983         if(asString === true){
9984             return fs;
9985         }
9986         return Roo.urlDecode(fs);
9987     },
9988
9989     /**
9990      * Returns the fields in this form as an object with key/value pairs.
9991      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9992      * @return {Object}
9993      */
9994     getFieldValues : function(with_hidden)
9995     {
9996         var items = this.getItems();
9997         var ret = {};
9998         items.each(function(f){
9999             
10000             if (!f.getName()) {
10001                 return;
10002             }
10003             
10004             var v = f.getValue();
10005             
10006             if (f.inputType =='radio') {
10007                 if (typeof(ret[f.getName()]) == 'undefined') {
10008                     ret[f.getName()] = ''; // empty..
10009                 }
10010
10011                 if (!f.el.dom.checked) {
10012                     return;
10013
10014                 }
10015                 v = f.el.dom.value;
10016
10017             }
10018             
10019             if(f.xtype == 'MoneyField'){
10020                 ret[f.currencyName] = f.getCurrency();
10021             }
10022
10023             // not sure if this supported any more..
10024             if ((typeof(v) == 'object') && f.getRawValue) {
10025                 v = f.getRawValue() ; // dates..
10026             }
10027             // combo boxes where name != hiddenName...
10028             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10029                 ret[f.name] = f.getRawValue();
10030             }
10031             ret[f.getName()] = v;
10032         });
10033
10034         return ret;
10035     },
10036
10037     /**
10038      * Clears all invalid messages in this form.
10039      * @return {BasicForm} this
10040      */
10041     clearInvalid : function(){
10042         var items = this.getItems();
10043
10044         items.each(function(f){
10045            f.clearInvalid();
10046         });
10047
10048         return this;
10049     },
10050
10051     /**
10052      * Resets this form.
10053      * @return {BasicForm} this
10054      */
10055     reset : function(){
10056         var items = this.getItems();
10057         items.each(function(f){
10058             f.reset();
10059         });
10060
10061         Roo.each(this.childForms || [], function (f) {
10062             f.reset();
10063         });
10064
10065
10066         return this;
10067     },
10068     
10069     getItems : function()
10070     {
10071         var r=new Roo.util.MixedCollection(false, function(o){
10072             return o.id || (o.id = Roo.id());
10073         });
10074         var iter = function(el) {
10075             if (el.inputEl) {
10076                 r.add(el);
10077             }
10078             if (!el.items) {
10079                 return;
10080             }
10081             Roo.each(el.items,function(e) {
10082                 iter(e);
10083             });
10084         };
10085
10086         iter(this);
10087         return r;
10088     },
10089     
10090     hideFields : function(items)
10091     {
10092         Roo.each(items, function(i){
10093             
10094             var f = this.findField(i);
10095             
10096             if(!f){
10097                 return;
10098             }
10099             
10100             f.hide();
10101             
10102         }, this);
10103     },
10104     
10105     showFields : function(items)
10106     {
10107         Roo.each(items, function(i){
10108             
10109             var f = this.findField(i);
10110             
10111             if(!f){
10112                 return;
10113             }
10114             
10115             f.show();
10116             
10117         }, this);
10118     }
10119
10120 });
10121
10122 Roo.apply(Roo.bootstrap.Form, {
10123     
10124     popover : {
10125         
10126         padding : 5,
10127         
10128         isApplied : false,
10129         
10130         isMasked : false,
10131         
10132         form : false,
10133         
10134         target : false,
10135         
10136         toolTip : false,
10137         
10138         intervalID : false,
10139         
10140         maskEl : false,
10141         
10142         apply : function()
10143         {
10144             if(this.isApplied){
10145                 return;
10146             }
10147             
10148             this.maskEl = {
10149                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10150                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10151                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10152                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10153             };
10154             
10155             this.maskEl.top.enableDisplayMode("block");
10156             this.maskEl.left.enableDisplayMode("block");
10157             this.maskEl.bottom.enableDisplayMode("block");
10158             this.maskEl.right.enableDisplayMode("block");
10159             
10160             this.toolTip = new Roo.bootstrap.Tooltip({
10161                 cls : 'roo-form-error-popover',
10162                 alignment : {
10163                     'left' : ['r-l', [-2,0], 'right'],
10164                     'right' : ['l-r', [2,0], 'left'],
10165                     'bottom' : ['tl-bl', [0,2], 'top'],
10166                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10167                 }
10168             });
10169             
10170             this.toolTip.render(Roo.get(document.body));
10171
10172             this.toolTip.el.enableDisplayMode("block");
10173             
10174             Roo.get(document.body).on('click', function(){
10175                 this.unmask();
10176             }, this);
10177             
10178             Roo.get(document.body).on('touchstart', function(){
10179                 this.unmask();
10180             }, this);
10181             
10182             this.isApplied = true
10183         },
10184         
10185         mask : function(form, target)
10186         {
10187             this.form = form;
10188             
10189             this.target = target;
10190             
10191             if(!this.form.errorMask || !target.el){
10192                 return;
10193             }
10194             
10195             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10196             
10197             Roo.log(scrollable);
10198             
10199             var ot = this.target.el.calcOffsetsTo(scrollable);
10200             
10201             var scrollTo = ot[1] - this.form.maskOffset;
10202             
10203             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10204             
10205             scrollable.scrollTo('top', scrollTo);
10206             
10207             var box = this.target.el.getBox();
10208             Roo.log(box);
10209             var zIndex = Roo.bootstrap.Modal.zIndex++;
10210
10211             
10212             this.maskEl.top.setStyle('position', 'absolute');
10213             this.maskEl.top.setStyle('z-index', zIndex);
10214             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10215             this.maskEl.top.setLeft(0);
10216             this.maskEl.top.setTop(0);
10217             this.maskEl.top.show();
10218             
10219             this.maskEl.left.setStyle('position', 'absolute');
10220             this.maskEl.left.setStyle('z-index', zIndex);
10221             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10222             this.maskEl.left.setLeft(0);
10223             this.maskEl.left.setTop(box.y - this.padding);
10224             this.maskEl.left.show();
10225
10226             this.maskEl.bottom.setStyle('position', 'absolute');
10227             this.maskEl.bottom.setStyle('z-index', zIndex);
10228             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10229             this.maskEl.bottom.setLeft(0);
10230             this.maskEl.bottom.setTop(box.bottom + this.padding);
10231             this.maskEl.bottom.show();
10232
10233             this.maskEl.right.setStyle('position', 'absolute');
10234             this.maskEl.right.setStyle('z-index', zIndex);
10235             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10236             this.maskEl.right.setLeft(box.right + this.padding);
10237             this.maskEl.right.setTop(box.y - this.padding);
10238             this.maskEl.right.show();
10239
10240             this.toolTip.bindEl = this.target.el;
10241
10242             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10243
10244             var tip = this.target.blankText;
10245
10246             if(this.target.getValue() !== '' ) {
10247                 
10248                 if (this.target.invalidText.length) {
10249                     tip = this.target.invalidText;
10250                 } else if (this.target.regexText.length){
10251                     tip = this.target.regexText;
10252                 }
10253             }
10254
10255             this.toolTip.show(tip);
10256
10257             this.intervalID = window.setInterval(function() {
10258                 Roo.bootstrap.Form.popover.unmask();
10259             }, 10000);
10260
10261             window.onwheel = function(){ return false;};
10262             
10263             (function(){ this.isMasked = true; }).defer(500, this);
10264             
10265         },
10266         
10267         unmask : function()
10268         {
10269             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10270                 return;
10271             }
10272             
10273             this.maskEl.top.setStyle('position', 'absolute');
10274             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10275             this.maskEl.top.hide();
10276
10277             this.maskEl.left.setStyle('position', 'absolute');
10278             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10279             this.maskEl.left.hide();
10280
10281             this.maskEl.bottom.setStyle('position', 'absolute');
10282             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10283             this.maskEl.bottom.hide();
10284
10285             this.maskEl.right.setStyle('position', 'absolute');
10286             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10287             this.maskEl.right.hide();
10288             
10289             this.toolTip.hide();
10290             
10291             this.toolTip.el.hide();
10292             
10293             window.onwheel = function(){ return true;};
10294             
10295             if(this.intervalID){
10296                 window.clearInterval(this.intervalID);
10297                 this.intervalID = false;
10298             }
10299             
10300             this.isMasked = false;
10301             
10302         }
10303         
10304     }
10305     
10306 });
10307
10308 /*
10309  * Based on:
10310  * Ext JS Library 1.1.1
10311  * Copyright(c) 2006-2007, Ext JS, LLC.
10312  *
10313  * Originally Released Under LGPL - original licence link has changed is not relivant.
10314  *
10315  * Fork - LGPL
10316  * <script type="text/javascript">
10317  */
10318 /**
10319  * @class Roo.form.VTypes
10320  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10321  * @singleton
10322  */
10323 Roo.form.VTypes = function(){
10324     // closure these in so they are only created once.
10325     var alpha = /^[a-zA-Z_]+$/;
10326     var alphanum = /^[a-zA-Z0-9_]+$/;
10327     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10328     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10329
10330     // All these messages and functions are configurable
10331     return {
10332         /**
10333          * The function used to validate email addresses
10334          * @param {String} value The email address
10335          */
10336         'email' : function(v){
10337             return email.test(v);
10338         },
10339         /**
10340          * The error text to display when the email validation function returns false
10341          * @type String
10342          */
10343         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10344         /**
10345          * The keystroke filter mask to be applied on email input
10346          * @type RegExp
10347          */
10348         'emailMask' : /[a-z0-9_\.\-@]/i,
10349
10350         /**
10351          * The function used to validate URLs
10352          * @param {String} value The URL
10353          */
10354         'url' : function(v){
10355             return url.test(v);
10356         },
10357         /**
10358          * The error text to display when the url validation function returns false
10359          * @type String
10360          */
10361         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10362         
10363         /**
10364          * The function used to validate alpha values
10365          * @param {String} value The value
10366          */
10367         'alpha' : function(v){
10368             return alpha.test(v);
10369         },
10370         /**
10371          * The error text to display when the alpha validation function returns false
10372          * @type String
10373          */
10374         'alphaText' : 'This field should only contain letters and _',
10375         /**
10376          * The keystroke filter mask to be applied on alpha input
10377          * @type RegExp
10378          */
10379         'alphaMask' : /[a-z_]/i,
10380
10381         /**
10382          * The function used to validate alphanumeric values
10383          * @param {String} value The value
10384          */
10385         'alphanum' : function(v){
10386             return alphanum.test(v);
10387         },
10388         /**
10389          * The error text to display when the alphanumeric validation function returns false
10390          * @type String
10391          */
10392         'alphanumText' : 'This field should only contain letters, numbers and _',
10393         /**
10394          * The keystroke filter mask to be applied on alphanumeric input
10395          * @type RegExp
10396          */
10397         'alphanumMask' : /[a-z0-9_]/i
10398     };
10399 }();/*
10400  * - LGPL
10401  *
10402  * Input
10403  * 
10404  */
10405
10406 /**
10407  * @class Roo.bootstrap.Input
10408  * @extends Roo.bootstrap.Component
10409  * Bootstrap Input class
10410  * @cfg {Boolean} disabled is it disabled
10411  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10412  * @cfg {String} name name of the input
10413  * @cfg {string} fieldLabel - the label associated
10414  * @cfg {string} placeholder - placeholder to put in text.
10415  * @cfg {string}  before - input group add on before
10416  * @cfg {string} after - input group add on after
10417  * @cfg {string} size - (lg|sm) or leave empty..
10418  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10419  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10420  * @cfg {Number} md colspan out of 12 for computer-sized screens
10421  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10422  * @cfg {string} value default value of the input
10423  * @cfg {Number} labelWidth set the width of label 
10424  * @cfg {Number} labellg set the width of label (1-12)
10425  * @cfg {Number} labelmd set the width of label (1-12)
10426  * @cfg {Number} labelsm set the width of label (1-12)
10427  * @cfg {Number} labelxs set the width of label (1-12)
10428  * @cfg {String} labelAlign (top|left)
10429  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10430  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10431  * @cfg {String} indicatorpos (left|right) default left
10432  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10433  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10434  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10435
10436  * @cfg {String} align (left|center|right) Default left
10437  * @cfg {Boolean} forceFeedback (true|false) Default false
10438  * 
10439  * @constructor
10440  * Create a new Input
10441  * @param {Object} config The config object
10442  */
10443
10444 Roo.bootstrap.Input = function(config){
10445     
10446     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10447     
10448     this.addEvents({
10449         /**
10450          * @event focus
10451          * Fires when this field receives input focus.
10452          * @param {Roo.form.Field} this
10453          */
10454         focus : true,
10455         /**
10456          * @event blur
10457          * Fires when this field loses input focus.
10458          * @param {Roo.form.Field} this
10459          */
10460         blur : true,
10461         /**
10462          * @event specialkey
10463          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10464          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10465          * @param {Roo.form.Field} this
10466          * @param {Roo.EventObject} e The event object
10467          */
10468         specialkey : true,
10469         /**
10470          * @event change
10471          * Fires just before the field blurs if the field value has changed.
10472          * @param {Roo.form.Field} this
10473          * @param {Mixed} newValue The new value
10474          * @param {Mixed} oldValue The original value
10475          */
10476         change : true,
10477         /**
10478          * @event invalid
10479          * Fires after the field has been marked as invalid.
10480          * @param {Roo.form.Field} this
10481          * @param {String} msg The validation message
10482          */
10483         invalid : true,
10484         /**
10485          * @event valid
10486          * Fires after the field has been validated with no errors.
10487          * @param {Roo.form.Field} this
10488          */
10489         valid : true,
10490          /**
10491          * @event keyup
10492          * Fires after the key up
10493          * @param {Roo.form.Field} this
10494          * @param {Roo.EventObject}  e The event Object
10495          */
10496         keyup : true
10497     });
10498 };
10499
10500 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10501      /**
10502      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10503       automatic validation (defaults to "keyup").
10504      */
10505     validationEvent : "keyup",
10506      /**
10507      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10508      */
10509     validateOnBlur : true,
10510     /**
10511      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10512      */
10513     validationDelay : 250,
10514      /**
10515      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10516      */
10517     focusClass : "x-form-focus",  // not needed???
10518     
10519        
10520     /**
10521      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     invalidClass : "has-warning",
10524     
10525     /**
10526      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10527      */
10528     validClass : "has-success",
10529     
10530     /**
10531      * @cfg {Boolean} hasFeedback (true|false) default true
10532      */
10533     hasFeedback : true,
10534     
10535     /**
10536      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     invalidFeedbackClass : "glyphicon-warning-sign",
10539     
10540     /**
10541      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10542      */
10543     validFeedbackClass : "glyphicon-ok",
10544     
10545     /**
10546      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10547      */
10548     selectOnFocus : false,
10549     
10550      /**
10551      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10552      */
10553     maskRe : null,
10554        /**
10555      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10556      */
10557     vtype : null,
10558     
10559       /**
10560      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10561      */
10562     disableKeyFilter : false,
10563     
10564        /**
10565      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10566      */
10567     disabled : false,
10568      /**
10569      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10570      */
10571     allowBlank : true,
10572     /**
10573      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10574      */
10575     blankText : "Please complete this mandatory field",
10576     
10577      /**
10578      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10579      */
10580     minLength : 0,
10581     /**
10582      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10583      */
10584     maxLength : Number.MAX_VALUE,
10585     /**
10586      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10587      */
10588     minLengthText : "The minimum length for this field is {0}",
10589     /**
10590      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10591      */
10592     maxLengthText : "The maximum length for this field is {0}",
10593   
10594     
10595     /**
10596      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10597      * If available, this function will be called only after the basic validators all return true, and will be passed the
10598      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10599      */
10600     validator : null,
10601     /**
10602      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10603      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10604      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10605      */
10606     regex : null,
10607     /**
10608      * @cfg {String} regexText -- Depricated - use Invalid Text
10609      */
10610     regexText : "",
10611     
10612     /**
10613      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10614      */
10615     invalidText : "",
10616     
10617     
10618     
10619     autocomplete: false,
10620     
10621     
10622     fieldLabel : '',
10623     inputType : 'text',
10624     
10625     name : false,
10626     placeholder: false,
10627     before : false,
10628     after : false,
10629     size : false,
10630     hasFocus : false,
10631     preventMark: false,
10632     isFormField : true,
10633     value : '',
10634     labelWidth : 2,
10635     labelAlign : false,
10636     readOnly : false,
10637     align : false,
10638     formatedValue : false,
10639     forceFeedback : false,
10640     
10641     indicatorpos : 'left',
10642     
10643     labellg : 0,
10644     labelmd : 0,
10645     labelsm : 0,
10646     labelxs : 0,
10647     
10648     capture : '',
10649     accept : '',
10650     
10651     parentLabelAlign : function()
10652     {
10653         var parent = this;
10654         while (parent.parent()) {
10655             parent = parent.parent();
10656             if (typeof(parent.labelAlign) !='undefined') {
10657                 return parent.labelAlign;
10658             }
10659         }
10660         return 'left';
10661         
10662     },
10663     
10664     getAutoCreate : function()
10665     {
10666         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10667         
10668         var id = Roo.id();
10669         
10670         var cfg = {};
10671         
10672         if(this.inputType != 'hidden'){
10673             cfg.cls = 'form-group' //input-group
10674         }
10675         
10676         var input =  {
10677             tag: 'input',
10678             id : id,
10679             type : this.inputType,
10680             value : this.value,
10681             cls : 'form-control',
10682             placeholder : this.placeholder || '',
10683             autocomplete : this.autocomplete || 'new-password'
10684         };
10685         if (this.inputType == 'file') {
10686             input.style = 'overflow:hidden'; // why not in CSS?
10687         }
10688         
10689         if(this.capture.length){
10690             input.capture = this.capture;
10691         }
10692         
10693         if(this.accept.length){
10694             input.accept = this.accept + "/*";
10695         }
10696         
10697         if(this.align){
10698             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10699         }
10700         
10701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10702             input.maxLength = this.maxLength;
10703         }
10704         
10705         if (this.disabled) {
10706             input.disabled=true;
10707         }
10708         
10709         if (this.readOnly) {
10710             input.readonly=true;
10711         }
10712         
10713         if (this.name) {
10714             input.name = this.name;
10715         }
10716         
10717         if (this.size) {
10718             input.cls += ' input-' + this.size;
10719         }
10720         
10721         var settings=this;
10722         ['xs','sm','md','lg'].map(function(size){
10723             if (settings[size]) {
10724                 cfg.cls += ' col-' + size + '-' + settings[size];
10725             }
10726         });
10727         
10728         var inputblock = input;
10729         
10730         var feedback = {
10731             tag: 'span',
10732             cls: 'glyphicon form-control-feedback'
10733         };
10734             
10735         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10736             
10737             inputblock = {
10738                 cls : 'has-feedback',
10739                 cn :  [
10740                     input,
10741                     feedback
10742                 ] 
10743             };  
10744         }
10745         
10746         if (this.before || this.after) {
10747             
10748             inputblock = {
10749                 cls : 'input-group',
10750                 cn :  [] 
10751             };
10752             
10753             if (this.before && typeof(this.before) == 'string') {
10754                 
10755                 inputblock.cn.push({
10756                     tag :'span',
10757                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10758                     html : this.before
10759                 });
10760             }
10761             if (this.before && typeof(this.before) == 'object') {
10762                 this.before = Roo.factory(this.before);
10763                 
10764                 inputblock.cn.push({
10765                     tag :'span',
10766                     cls : 'roo-input-before input-group-prepend   input-group-' +
10767                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10768                 });
10769             }
10770             
10771             inputblock.cn.push(input);
10772             
10773             if (this.after && typeof(this.after) == 'string') {
10774                 inputblock.cn.push({
10775                     tag :'span',
10776                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10777                     html : this.after
10778                 });
10779             }
10780             if (this.after && typeof(this.after) == 'object') {
10781                 this.after = Roo.factory(this.after);
10782                 
10783                 inputblock.cn.push({
10784                     tag :'span',
10785                     cls : 'roo-input-after input-group-append  input-group-' +
10786                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10787                 });
10788             }
10789             
10790             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10791                 inputblock.cls += ' has-feedback';
10792                 inputblock.cn.push(feedback);
10793             }
10794         };
10795         var indicator = {
10796             tag : 'i',
10797             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10798             tooltip : 'This field is required'
10799         };
10800         if (this.allowBlank ) {
10801             indicator.style = this.allowBlank ? ' display:none' : '';
10802         }
10803         if (align ==='left' && this.fieldLabel.length) {
10804             
10805             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10806             
10807             cfg.cn = [
10808                 indicator,
10809                 {
10810                     tag: 'label',
10811                     'for' :  id,
10812                     cls : 'control-label col-form-label',
10813                     html : this.fieldLabel
10814
10815                 },
10816                 {
10817                     cls : "", 
10818                     cn: [
10819                         inputblock
10820                     ]
10821                 }
10822             ];
10823             
10824             var labelCfg = cfg.cn[1];
10825             var contentCfg = cfg.cn[2];
10826             
10827             if(this.indicatorpos == 'right'){
10828                 cfg.cn = [
10829                     {
10830                         tag: 'label',
10831                         'for' :  id,
10832                         cls : 'control-label col-form-label',
10833                         cn : [
10834                             {
10835                                 tag : 'span',
10836                                 html : this.fieldLabel
10837                             },
10838                             indicator
10839                         ]
10840                     },
10841                     {
10842                         cls : "",
10843                         cn: [
10844                             inputblock
10845                         ]
10846                     }
10847
10848                 ];
10849                 
10850                 labelCfg = cfg.cn[0];
10851                 contentCfg = cfg.cn[1];
10852             
10853             }
10854             
10855             if(this.labelWidth > 12){
10856                 labelCfg.style = "width: " + this.labelWidth + 'px';
10857             }
10858             
10859             if(this.labelWidth < 13 && this.labelmd == 0){
10860                 this.labelmd = this.labelWidth;
10861             }
10862             
10863             if(this.labellg > 0){
10864                 labelCfg.cls += ' col-lg-' + this.labellg;
10865                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10866             }
10867             
10868             if(this.labelmd > 0){
10869                 labelCfg.cls += ' col-md-' + this.labelmd;
10870                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10871             }
10872             
10873             if(this.labelsm > 0){
10874                 labelCfg.cls += ' col-sm-' + this.labelsm;
10875                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10876             }
10877             
10878             if(this.labelxs > 0){
10879                 labelCfg.cls += ' col-xs-' + this.labelxs;
10880                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10881             }
10882             
10883             
10884         } else if ( this.fieldLabel.length) {
10885                 
10886             
10887             
10888             cfg.cn = [
10889                 {
10890                     tag : 'i',
10891                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10892                     tooltip : 'This field is required',
10893                     style : this.allowBlank ? ' display:none' : '' 
10894                 },
10895                 {
10896                     tag: 'label',
10897                    //cls : 'input-group-addon',
10898                     html : this.fieldLabel
10899
10900                 },
10901
10902                inputblock
10903
10904            ];
10905            
10906            if(this.indicatorpos == 'right'){
10907        
10908                 cfg.cn = [
10909                     {
10910                         tag: 'label',
10911                        //cls : 'input-group-addon',
10912                         html : this.fieldLabel
10913
10914                     },
10915                     {
10916                         tag : 'i',
10917                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10918                         tooltip : 'This field is required',
10919                         style : this.allowBlank ? ' display:none' : '' 
10920                     },
10921
10922                    inputblock
10923
10924                ];
10925
10926             }
10927
10928         } else {
10929             
10930             cfg.cn = [
10931
10932                     inputblock
10933
10934             ];
10935                 
10936                 
10937         };
10938         
10939         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10940            cfg.cls += ' navbar-form';
10941         }
10942         
10943         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10944             // on BS4 we do this only if not form 
10945             cfg.cls += ' navbar-form';
10946             cfg.tag = 'li';
10947         }
10948         
10949         return cfg;
10950         
10951     },
10952     /**
10953      * return the real input element.
10954      */
10955     inputEl: function ()
10956     {
10957         return this.el.select('input.form-control',true).first();
10958     },
10959     
10960     tooltipEl : function()
10961     {
10962         return this.inputEl();
10963     },
10964     
10965     indicatorEl : function()
10966     {
10967         if (Roo.bootstrap.version == 4) {
10968             return false; // not enabled in v4 yet.
10969         }
10970         
10971         var indicator = this.el.select('i.roo-required-indicator',true).first();
10972         
10973         if(!indicator){
10974             return false;
10975         }
10976         
10977         return indicator;
10978         
10979     },
10980     
10981     setDisabled : function(v)
10982     {
10983         var i  = this.inputEl().dom;
10984         if (!v) {
10985             i.removeAttribute('disabled');
10986             return;
10987             
10988         }
10989         i.setAttribute('disabled','true');
10990     },
10991     initEvents : function()
10992     {
10993           
10994         this.inputEl().on("keydown" , this.fireKey,  this);
10995         this.inputEl().on("focus", this.onFocus,  this);
10996         this.inputEl().on("blur", this.onBlur,  this);
10997         
10998         this.inputEl().relayEvent('keyup', this);
10999         
11000         this.indicator = this.indicatorEl();
11001         
11002         if(this.indicator){
11003             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11004         }
11005  
11006         // reference to original value for reset
11007         this.originalValue = this.getValue();
11008         //Roo.form.TextField.superclass.initEvents.call(this);
11009         if(this.validationEvent == 'keyup'){
11010             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11011             this.inputEl().on('keyup', this.filterValidation, this);
11012         }
11013         else if(this.validationEvent !== false){
11014             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11015         }
11016         
11017         if(this.selectOnFocus){
11018             this.on("focus", this.preFocus, this);
11019             
11020         }
11021         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11022             this.inputEl().on("keypress", this.filterKeys, this);
11023         } else {
11024             this.inputEl().relayEvent('keypress', this);
11025         }
11026        /* if(this.grow){
11027             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11028             this.el.on("click", this.autoSize,  this);
11029         }
11030         */
11031         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11032             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11033         }
11034         
11035         if (typeof(this.before) == 'object') {
11036             this.before.render(this.el.select('.roo-input-before',true).first());
11037         }
11038         if (typeof(this.after) == 'object') {
11039             this.after.render(this.el.select('.roo-input-after',true).first());
11040         }
11041         
11042         this.inputEl().on('change', this.onChange, this);
11043         
11044     },
11045     filterValidation : function(e){
11046         if(!e.isNavKeyPress()){
11047             this.validationTask.delay(this.validationDelay);
11048         }
11049     },
11050      /**
11051      * Validates the field value
11052      * @return {Boolean} True if the value is valid, else false
11053      */
11054     validate : function(){
11055         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11056         if(this.disabled || this.validateValue(this.getRawValue())){
11057             this.markValid();
11058             return true;
11059         }
11060         
11061         this.markInvalid();
11062         return false;
11063     },
11064     
11065     
11066     /**
11067      * Validates a value according to the field's validation rules and marks the field as invalid
11068      * if the validation fails
11069      * @param {Mixed} value The value to validate
11070      * @return {Boolean} True if the value is valid, else false
11071      */
11072     validateValue : function(value)
11073     {
11074         if(this.getVisibilityEl().hasClass('hidden')){
11075             return true;
11076         }
11077         
11078         if(value.length < 1)  { // if it's blank
11079             if(this.allowBlank){
11080                 return true;
11081             }
11082             return false;
11083         }
11084         
11085         if(value.length < this.minLength){
11086             return false;
11087         }
11088         if(value.length > this.maxLength){
11089             return false;
11090         }
11091         if(this.vtype){
11092             var vt = Roo.form.VTypes;
11093             if(!vt[this.vtype](value, this)){
11094                 return false;
11095             }
11096         }
11097         if(typeof this.validator == "function"){
11098             var msg = this.validator(value);
11099             if(msg !== true){
11100                 return false;
11101             }
11102             if (typeof(msg) == 'string') {
11103                 this.invalidText = msg;
11104             }
11105         }
11106         
11107         if(this.regex && !this.regex.test(value)){
11108             return false;
11109         }
11110         
11111         return true;
11112     },
11113     
11114      // private
11115     fireKey : function(e){
11116         //Roo.log('field ' + e.getKey());
11117         if(e.isNavKeyPress()){
11118             this.fireEvent("specialkey", this, e);
11119         }
11120     },
11121     focus : function (selectText){
11122         if(this.rendered){
11123             this.inputEl().focus();
11124             if(selectText === true){
11125                 this.inputEl().dom.select();
11126             }
11127         }
11128         return this;
11129     } ,
11130     
11131     onFocus : function(){
11132         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11133            // this.el.addClass(this.focusClass);
11134         }
11135         if(!this.hasFocus){
11136             this.hasFocus = true;
11137             this.startValue = this.getValue();
11138             this.fireEvent("focus", this);
11139         }
11140     },
11141     
11142     beforeBlur : Roo.emptyFn,
11143
11144     
11145     // private
11146     onBlur : function(){
11147         this.beforeBlur();
11148         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11149             //this.el.removeClass(this.focusClass);
11150         }
11151         this.hasFocus = false;
11152         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11153             this.validate();
11154         }
11155         var v = this.getValue();
11156         if(String(v) !== String(this.startValue)){
11157             this.fireEvent('change', this, v, this.startValue);
11158         }
11159         this.fireEvent("blur", this);
11160     },
11161     
11162     onChange : function(e)
11163     {
11164         var v = this.getValue();
11165         if(String(v) !== String(this.startValue)){
11166             this.fireEvent('change', this, v, this.startValue);
11167         }
11168         
11169     },
11170     
11171     /**
11172      * Resets the current field value to the originally loaded value and clears any validation messages
11173      */
11174     reset : function(){
11175         this.setValue(this.originalValue);
11176         this.validate();
11177     },
11178      /**
11179      * Returns the name of the field
11180      * @return {Mixed} name The name field
11181      */
11182     getName: function(){
11183         return this.name;
11184     },
11185      /**
11186      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11187      * @return {Mixed} value The field value
11188      */
11189     getValue : function(){
11190         
11191         var v = this.inputEl().getValue();
11192         
11193         return v;
11194     },
11195     /**
11196      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11197      * @return {Mixed} value The field value
11198      */
11199     getRawValue : function(){
11200         var v = this.inputEl().getValue();
11201         
11202         return v;
11203     },
11204     
11205     /**
11206      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11207      * @param {Mixed} value The value to set
11208      */
11209     setRawValue : function(v){
11210         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11211     },
11212     
11213     selectText : function(start, end){
11214         var v = this.getRawValue();
11215         if(v.length > 0){
11216             start = start === undefined ? 0 : start;
11217             end = end === undefined ? v.length : end;
11218             var d = this.inputEl().dom;
11219             if(d.setSelectionRange){
11220                 d.setSelectionRange(start, end);
11221             }else if(d.createTextRange){
11222                 var range = d.createTextRange();
11223                 range.moveStart("character", start);
11224                 range.moveEnd("character", v.length-end);
11225                 range.select();
11226             }
11227         }
11228     },
11229     
11230     /**
11231      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11232      * @param {Mixed} value The value to set
11233      */
11234     setValue : function(v){
11235         this.value = v;
11236         if(this.rendered){
11237             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11238             this.validate();
11239         }
11240     },
11241     
11242     /*
11243     processValue : function(value){
11244         if(this.stripCharsRe){
11245             var newValue = value.replace(this.stripCharsRe, '');
11246             if(newValue !== value){
11247                 this.setRawValue(newValue);
11248                 return newValue;
11249             }
11250         }
11251         return value;
11252     },
11253   */
11254     preFocus : function(){
11255         
11256         if(this.selectOnFocus){
11257             this.inputEl().dom.select();
11258         }
11259     },
11260     filterKeys : function(e){
11261         var k = e.getKey();
11262         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11263             return;
11264         }
11265         var c = e.getCharCode(), cc = String.fromCharCode(c);
11266         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11267             return;
11268         }
11269         if(!this.maskRe.test(cc)){
11270             e.stopEvent();
11271         }
11272     },
11273      /**
11274      * Clear any invalid styles/messages for this field
11275      */
11276     clearInvalid : function(){
11277         
11278         if(!this.el || this.preventMark){ // not rendered
11279             return;
11280         }
11281         
11282         
11283         this.el.removeClass([this.invalidClass, 'is-invalid']);
11284         
11285         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11286             
11287             var feedback = this.el.select('.form-control-feedback', true).first();
11288             
11289             if(feedback){
11290                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11291             }
11292             
11293         }
11294         
11295         if(this.indicator){
11296             this.indicator.removeClass('visible');
11297             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11298         }
11299         
11300         this.fireEvent('valid', this);
11301     },
11302     
11303      /**
11304      * Mark this field as valid
11305      */
11306     markValid : function()
11307     {
11308         if(!this.el  || this.preventMark){ // not rendered...
11309             return;
11310         }
11311         
11312         this.el.removeClass([this.invalidClass, this.validClass]);
11313         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11314
11315         var feedback = this.el.select('.form-control-feedback', true).first();
11316             
11317         if(feedback){
11318             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11319         }
11320         
11321         if(this.indicator){
11322             this.indicator.removeClass('visible');
11323             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11324         }
11325         
11326         if(this.disabled){
11327             return;
11328         }
11329         
11330            
11331         if(this.allowBlank && !this.getRawValue().length){
11332             return;
11333         }
11334         if (Roo.bootstrap.version == 3) {
11335             this.el.addClass(this.validClass);
11336         } else {
11337             this.inputEl().addClass('is-valid');
11338         }
11339
11340         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11341             
11342             var feedback = this.el.select('.form-control-feedback', true).first();
11343             
11344             if(feedback){
11345                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11346                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11347             }
11348             
11349         }
11350         
11351         this.fireEvent('valid', this);
11352     },
11353     
11354      /**
11355      * Mark this field as invalid
11356      * @param {String} msg The validation message
11357      */
11358     markInvalid : function(msg)
11359     {
11360         if(!this.el  || this.preventMark){ // not rendered
11361             return;
11362         }
11363         
11364         this.el.removeClass([this.invalidClass, this.validClass]);
11365         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11366         
11367         var feedback = this.el.select('.form-control-feedback', true).first();
11368             
11369         if(feedback){
11370             this.el.select('.form-control-feedback', true).first().removeClass(
11371                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11372         }
11373
11374         if(this.disabled){
11375             return;
11376         }
11377         
11378         if(this.allowBlank && !this.getRawValue().length){
11379             return;
11380         }
11381         
11382         if(this.indicator){
11383             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11384             this.indicator.addClass('visible');
11385         }
11386         if (Roo.bootstrap.version == 3) {
11387             this.el.addClass(this.invalidClass);
11388         } else {
11389             this.inputEl().addClass('is-invalid');
11390         }
11391         
11392         
11393         
11394         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11395             
11396             var feedback = this.el.select('.form-control-feedback', true).first();
11397             
11398             if(feedback){
11399                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11400                 
11401                 if(this.getValue().length || this.forceFeedback){
11402                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11403                 }
11404                 
11405             }
11406             
11407         }
11408         
11409         this.fireEvent('invalid', this, msg);
11410     },
11411     // private
11412     SafariOnKeyDown : function(event)
11413     {
11414         // this is a workaround for a password hang bug on chrome/ webkit.
11415         if (this.inputEl().dom.type != 'password') {
11416             return;
11417         }
11418         
11419         var isSelectAll = false;
11420         
11421         if(this.inputEl().dom.selectionEnd > 0){
11422             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11423         }
11424         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11425             event.preventDefault();
11426             this.setValue('');
11427             return;
11428         }
11429         
11430         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11431             
11432             event.preventDefault();
11433             // this is very hacky as keydown always get's upper case.
11434             //
11435             var cc = String.fromCharCode(event.getCharCode());
11436             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11437             
11438         }
11439     },
11440     adjustWidth : function(tag, w){
11441         tag = tag.toLowerCase();
11442         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11443             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11444                 if(tag == 'input'){
11445                     return w + 2;
11446                 }
11447                 if(tag == 'textarea'){
11448                     return w-2;
11449                 }
11450             }else if(Roo.isOpera){
11451                 if(tag == 'input'){
11452                     return w + 2;
11453                 }
11454                 if(tag == 'textarea'){
11455                     return w-2;
11456                 }
11457             }
11458         }
11459         return w;
11460     },
11461     
11462     setFieldLabel : function(v)
11463     {
11464         if(!this.rendered){
11465             return;
11466         }
11467         
11468         if(this.indicatorEl()){
11469             var ar = this.el.select('label > span',true);
11470             
11471             if (ar.elements.length) {
11472                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11473                 this.fieldLabel = v;
11474                 return;
11475             }
11476             
11477             var br = this.el.select('label',true);
11478             
11479             if(br.elements.length) {
11480                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11481                 this.fieldLabel = v;
11482                 return;
11483             }
11484             
11485             Roo.log('Cannot Found any of label > span || label in input');
11486             return;
11487         }
11488         
11489         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11490         this.fieldLabel = v;
11491         
11492         
11493     }
11494 });
11495
11496  
11497 /*
11498  * - LGPL
11499  *
11500  * Input
11501  * 
11502  */
11503
11504 /**
11505  * @class Roo.bootstrap.TextArea
11506  * @extends Roo.bootstrap.Input
11507  * Bootstrap TextArea class
11508  * @cfg {Number} cols Specifies the visible width of a text area
11509  * @cfg {Number} rows Specifies the visible number of lines in a text area
11510  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11511  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11512  * @cfg {string} html text
11513  * 
11514  * @constructor
11515  * Create a new TextArea
11516  * @param {Object} config The config object
11517  */
11518
11519 Roo.bootstrap.TextArea = function(config){
11520     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11521    
11522 };
11523
11524 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11525      
11526     cols : false,
11527     rows : 5,
11528     readOnly : false,
11529     warp : 'soft',
11530     resize : false,
11531     value: false,
11532     html: false,
11533     
11534     getAutoCreate : function(){
11535         
11536         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11537         
11538         var id = Roo.id();
11539         
11540         var cfg = {};
11541         
11542         if(this.inputType != 'hidden'){
11543             cfg.cls = 'form-group' //input-group
11544         }
11545         
11546         var input =  {
11547             tag: 'textarea',
11548             id : id,
11549             warp : this.warp,
11550             rows : this.rows,
11551             value : this.value || '',
11552             html: this.html || '',
11553             cls : 'form-control',
11554             placeholder : this.placeholder || '' 
11555             
11556         };
11557         
11558         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11559             input.maxLength = this.maxLength;
11560         }
11561         
11562         if(this.resize){
11563             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11564         }
11565         
11566         if(this.cols){
11567             input.cols = this.cols;
11568         }
11569         
11570         if (this.readOnly) {
11571             input.readonly = true;
11572         }
11573         
11574         if (this.name) {
11575             input.name = this.name;
11576         }
11577         
11578         if (this.size) {
11579             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11580         }
11581         
11582         var settings=this;
11583         ['xs','sm','md','lg'].map(function(size){
11584             if (settings[size]) {
11585                 cfg.cls += ' col-' + size + '-' + settings[size];
11586             }
11587         });
11588         
11589         var inputblock = input;
11590         
11591         if(this.hasFeedback && !this.allowBlank){
11592             
11593             var feedback = {
11594                 tag: 'span',
11595                 cls: 'glyphicon form-control-feedback'
11596             };
11597
11598             inputblock = {
11599                 cls : 'has-feedback',
11600                 cn :  [
11601                     input,
11602                     feedback
11603                 ] 
11604             };  
11605         }
11606         
11607         
11608         if (this.before || this.after) {
11609             
11610             inputblock = {
11611                 cls : 'input-group',
11612                 cn :  [] 
11613             };
11614             if (this.before) {
11615                 inputblock.cn.push({
11616                     tag :'span',
11617                     cls : 'input-group-addon',
11618                     html : this.before
11619                 });
11620             }
11621             
11622             inputblock.cn.push(input);
11623             
11624             if(this.hasFeedback && !this.allowBlank){
11625                 inputblock.cls += ' has-feedback';
11626                 inputblock.cn.push(feedback);
11627             }
11628             
11629             if (this.after) {
11630                 inputblock.cn.push({
11631                     tag :'span',
11632                     cls : 'input-group-addon',
11633                     html : this.after
11634                 });
11635             }
11636             
11637         }
11638         
11639         if (align ==='left' && this.fieldLabel.length) {
11640             cfg.cn = [
11641                 {
11642                     tag: 'label',
11643                     'for' :  id,
11644                     cls : 'control-label',
11645                     html : this.fieldLabel
11646                 },
11647                 {
11648                     cls : "",
11649                     cn: [
11650                         inputblock
11651                     ]
11652                 }
11653
11654             ];
11655             
11656             if(this.labelWidth > 12){
11657                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11658             }
11659
11660             if(this.labelWidth < 13 && this.labelmd == 0){
11661                 this.labelmd = this.labelWidth;
11662             }
11663
11664             if(this.labellg > 0){
11665                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11666                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11667             }
11668
11669             if(this.labelmd > 0){
11670                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11671                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11672             }
11673
11674             if(this.labelsm > 0){
11675                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11676                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11677             }
11678
11679             if(this.labelxs > 0){
11680                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11681                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11682             }
11683             
11684         } else if ( this.fieldLabel.length) {
11685             cfg.cn = [
11686
11687                {
11688                    tag: 'label',
11689                    //cls : 'input-group-addon',
11690                    html : this.fieldLabel
11691
11692                },
11693
11694                inputblock
11695
11696            ];
11697
11698         } else {
11699
11700             cfg.cn = [
11701
11702                 inputblock
11703
11704             ];
11705                 
11706         }
11707         
11708         if (this.disabled) {
11709             input.disabled=true;
11710         }
11711         
11712         return cfg;
11713         
11714     },
11715     /**
11716      * return the real textarea element.
11717      */
11718     inputEl: function ()
11719     {
11720         return this.el.select('textarea.form-control',true).first();
11721     },
11722     
11723     /**
11724      * Clear any invalid styles/messages for this field
11725      */
11726     clearInvalid : function()
11727     {
11728         
11729         if(!this.el || this.preventMark){ // not rendered
11730             return;
11731         }
11732         
11733         var label = this.el.select('label', true).first();
11734         var icon = this.el.select('i.fa-star', true).first();
11735         
11736         if(label && icon){
11737             icon.remove();
11738         }
11739         this.el.removeClass( this.validClass);
11740         this.inputEl().removeClass('is-invalid');
11741          
11742         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11743             
11744             var feedback = this.el.select('.form-control-feedback', true).first();
11745             
11746             if(feedback){
11747                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11748             }
11749             
11750         }
11751         
11752         this.fireEvent('valid', this);
11753     },
11754     
11755      /**
11756      * Mark this field as valid
11757      */
11758     markValid : function()
11759     {
11760         if(!this.el  || this.preventMark){ // not rendered
11761             return;
11762         }
11763         
11764         this.el.removeClass([this.invalidClass, this.validClass]);
11765         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11766         
11767         var feedback = this.el.select('.form-control-feedback', true).first();
11768             
11769         if(feedback){
11770             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11771         }
11772
11773         if(this.disabled || this.allowBlank){
11774             return;
11775         }
11776         
11777         var label = this.el.select('label', true).first();
11778         var icon = this.el.select('i.fa-star', true).first();
11779         
11780         if(label && icon){
11781             icon.remove();
11782         }
11783         if (Roo.bootstrap.version == 3) {
11784             this.el.addClass(this.validClass);
11785         } else {
11786             this.inputEl().addClass('is-valid');
11787         }
11788         
11789         
11790         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11791             
11792             var feedback = this.el.select('.form-control-feedback', true).first();
11793             
11794             if(feedback){
11795                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11796                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11797             }
11798             
11799         }
11800         
11801         this.fireEvent('valid', this);
11802     },
11803     
11804      /**
11805      * Mark this field as invalid
11806      * @param {String} msg The validation message
11807      */
11808     markInvalid : function(msg)
11809     {
11810         if(!this.el  || this.preventMark){ // not rendered
11811             return;
11812         }
11813         
11814         this.el.removeClass([this.invalidClass, this.validClass]);
11815         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11816         
11817         var feedback = this.el.select('.form-control-feedback', true).first();
11818             
11819         if(feedback){
11820             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11821         }
11822
11823         if(this.disabled || this.allowBlank){
11824             return;
11825         }
11826         
11827         var label = this.el.select('label', true).first();
11828         var icon = this.el.select('i.fa-star', true).first();
11829         
11830         if(!this.getValue().length && label && !icon){
11831             this.el.createChild({
11832                 tag : 'i',
11833                 cls : 'text-danger fa fa-lg fa-star',
11834                 tooltip : 'This field is required',
11835                 style : 'margin-right:5px;'
11836             }, label, true);
11837         }
11838         
11839         if (Roo.bootstrap.version == 3) {
11840             this.el.addClass(this.invalidClass);
11841         } else {
11842             this.inputEl().addClass('is-invalid');
11843         }
11844         
11845         // fixme ... this may be depricated need to test..
11846         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11847             
11848             var feedback = this.el.select('.form-control-feedback', true).first();
11849             
11850             if(feedback){
11851                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11852                 
11853                 if(this.getValue().length || this.forceFeedback){
11854                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11855                 }
11856                 
11857             }
11858             
11859         }
11860         
11861         this.fireEvent('invalid', this, msg);
11862     }
11863 });
11864
11865  
11866 /*
11867  * - LGPL
11868  *
11869  * trigger field - base class for combo..
11870  * 
11871  */
11872  
11873 /**
11874  * @class Roo.bootstrap.TriggerField
11875  * @extends Roo.bootstrap.Input
11876  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11877  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11878  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11879  * for which you can provide a custom implementation.  For example:
11880  * <pre><code>
11881 var trigger = new Roo.bootstrap.TriggerField();
11882 trigger.onTriggerClick = myTriggerFn;
11883 trigger.applyTo('my-field');
11884 </code></pre>
11885  *
11886  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11887  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11888  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11889  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11890  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11891
11892  * @constructor
11893  * Create a new TriggerField.
11894  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11895  * to the base TextField)
11896  */
11897 Roo.bootstrap.TriggerField = function(config){
11898     this.mimicing = false;
11899     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11900 };
11901
11902 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11903     /**
11904      * @cfg {String} triggerClass A CSS class to apply to the trigger
11905      */
11906      /**
11907      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11908      */
11909     hideTrigger:false,
11910
11911     /**
11912      * @cfg {Boolean} removable (true|false) special filter default false
11913      */
11914     removable : false,
11915     
11916     /** @cfg {Boolean} grow @hide */
11917     /** @cfg {Number} growMin @hide */
11918     /** @cfg {Number} growMax @hide */
11919
11920     /**
11921      * @hide 
11922      * @method
11923      */
11924     autoSize: Roo.emptyFn,
11925     // private
11926     monitorTab : true,
11927     // private
11928     deferHeight : true,
11929
11930     
11931     actionMode : 'wrap',
11932     
11933     caret : false,
11934     
11935     
11936     getAutoCreate : function(){
11937        
11938         var align = this.labelAlign || this.parentLabelAlign();
11939         
11940         var id = Roo.id();
11941         
11942         var cfg = {
11943             cls: 'form-group' //input-group
11944         };
11945         
11946         
11947         var input =  {
11948             tag: 'input',
11949             id : id,
11950             type : this.inputType,
11951             cls : 'form-control',
11952             autocomplete: 'new-password',
11953             placeholder : this.placeholder || '' 
11954             
11955         };
11956         if (this.name) {
11957             input.name = this.name;
11958         }
11959         if (this.size) {
11960             input.cls += ' input-' + this.size;
11961         }
11962         
11963         if (this.disabled) {
11964             input.disabled=true;
11965         }
11966         
11967         var inputblock = input;
11968         
11969         if(this.hasFeedback && !this.allowBlank){
11970             
11971             var feedback = {
11972                 tag: 'span',
11973                 cls: 'glyphicon form-control-feedback'
11974             };
11975             
11976             if(this.removable && !this.editable  ){
11977                 inputblock = {
11978                     cls : 'has-feedback',
11979                     cn :  [
11980                         inputblock,
11981                         {
11982                             tag: 'button',
11983                             html : 'x',
11984                             cls : 'roo-combo-removable-btn close'
11985                         },
11986                         feedback
11987                     ] 
11988                 };
11989             } else {
11990                 inputblock = {
11991                     cls : 'has-feedback',
11992                     cn :  [
11993                         inputblock,
11994                         feedback
11995                     ] 
11996                 };
11997             }
11998
11999         } else {
12000             if(this.removable && !this.editable ){
12001                 inputblock = {
12002                     cls : 'roo-removable',
12003                     cn :  [
12004                         inputblock,
12005                         {
12006                             tag: 'button',
12007                             html : 'x',
12008                             cls : 'roo-combo-removable-btn close'
12009                         }
12010                     ] 
12011                 };
12012             }
12013         }
12014         
12015         if (this.before || this.after) {
12016             
12017             inputblock = {
12018                 cls : 'input-group',
12019                 cn :  [] 
12020             };
12021             if (this.before) {
12022                 inputblock.cn.push({
12023                     tag :'span',
12024                     cls : 'input-group-addon input-group-prepend input-group-text',
12025                     html : this.before
12026                 });
12027             }
12028             
12029             inputblock.cn.push(input);
12030             
12031             if(this.hasFeedback && !this.allowBlank){
12032                 inputblock.cls += ' has-feedback';
12033                 inputblock.cn.push(feedback);
12034             }
12035             
12036             if (this.after) {
12037                 inputblock.cn.push({
12038                     tag :'span',
12039                     cls : 'input-group-addon input-group-append input-group-text',
12040                     html : this.after
12041                 });
12042             }
12043             
12044         };
12045         
12046       
12047         
12048         var ibwrap = inputblock;
12049         
12050         if(this.multiple){
12051             ibwrap = {
12052                 tag: 'ul',
12053                 cls: 'roo-select2-choices',
12054                 cn:[
12055                     {
12056                         tag: 'li',
12057                         cls: 'roo-select2-search-field',
12058                         cn: [
12059
12060                             inputblock
12061                         ]
12062                     }
12063                 ]
12064             };
12065                 
12066         }
12067         
12068         var combobox = {
12069             cls: 'roo-select2-container input-group',
12070             cn: [
12071                  {
12072                     tag: 'input',
12073                     type : 'hidden',
12074                     cls: 'form-hidden-field'
12075                 },
12076                 ibwrap
12077             ]
12078         };
12079         
12080         if(!this.multiple && this.showToggleBtn){
12081             
12082             var caret = {
12083                         tag: 'span',
12084                         cls: 'caret'
12085              };
12086             if (this.caret != false) {
12087                 caret = {
12088                      tag: 'i',
12089                      cls: 'fa fa-' + this.caret
12090                 };
12091                 
12092             }
12093             
12094             combobox.cn.push({
12095                 tag :'span',
12096                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12097                 cn : [
12098                     Roo.bootstrap.version == 3 ? caret : '',
12099                     {
12100                         tag: 'span',
12101                         cls: 'combobox-clear',
12102                         cn  : [
12103                             {
12104                                 tag : 'i',
12105                                 cls: 'icon-remove'
12106                             }
12107                         ]
12108                     }
12109                 ]
12110
12111             })
12112         }
12113         
12114         if(this.multiple){
12115             combobox.cls += ' roo-select2-container-multi';
12116         }
12117          var indicator = {
12118             tag : 'i',
12119             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12120             tooltip : 'This field is required'
12121         };
12122         if (Roo.bootstrap.version == 4) {
12123             indicator = {
12124                 tag : 'i',
12125                 style : 'display:none'
12126             };
12127         }
12128         
12129         
12130         if (align ==='left' && this.fieldLabel.length) {
12131             
12132             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12133
12134             cfg.cn = [
12135                 indicator,
12136                 {
12137                     tag: 'label',
12138                     'for' :  id,
12139                     cls : 'control-label',
12140                     html : this.fieldLabel
12141
12142                 },
12143                 {
12144                     cls : "", 
12145                     cn: [
12146                         combobox
12147                     ]
12148                 }
12149
12150             ];
12151             
12152             var labelCfg = cfg.cn[1];
12153             var contentCfg = cfg.cn[2];
12154             
12155             if(this.indicatorpos == 'right'){
12156                 cfg.cn = [
12157                     {
12158                         tag: 'label',
12159                         'for' :  id,
12160                         cls : 'control-label',
12161                         cn : [
12162                             {
12163                                 tag : 'span',
12164                                 html : this.fieldLabel
12165                             },
12166                             indicator
12167                         ]
12168                     },
12169                     {
12170                         cls : "", 
12171                         cn: [
12172                             combobox
12173                         ]
12174                     }
12175
12176                 ];
12177                 
12178                 labelCfg = cfg.cn[0];
12179                 contentCfg = cfg.cn[1];
12180             }
12181             
12182             if(this.labelWidth > 12){
12183                 labelCfg.style = "width: " + this.labelWidth + 'px';
12184             }
12185             
12186             if(this.labelWidth < 13 && this.labelmd == 0){
12187                 this.labelmd = this.labelWidth;
12188             }
12189             
12190             if(this.labellg > 0){
12191                 labelCfg.cls += ' col-lg-' + this.labellg;
12192                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12193             }
12194             
12195             if(this.labelmd > 0){
12196                 labelCfg.cls += ' col-md-' + this.labelmd;
12197                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12198             }
12199             
12200             if(this.labelsm > 0){
12201                 labelCfg.cls += ' col-sm-' + this.labelsm;
12202                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12203             }
12204             
12205             if(this.labelxs > 0){
12206                 labelCfg.cls += ' col-xs-' + this.labelxs;
12207                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12208             }
12209             
12210         } else if ( this.fieldLabel.length) {
12211 //                Roo.log(" label");
12212             cfg.cn = [
12213                 indicator,
12214                {
12215                    tag: 'label',
12216                    //cls : 'input-group-addon',
12217                    html : this.fieldLabel
12218
12219                },
12220
12221                combobox
12222
12223             ];
12224             
12225             if(this.indicatorpos == 'right'){
12226                 
12227                 cfg.cn = [
12228                     {
12229                        tag: 'label',
12230                        cn : [
12231                            {
12232                                tag : 'span',
12233                                html : this.fieldLabel
12234                            },
12235                            indicator
12236                        ]
12237
12238                     },
12239                     combobox
12240
12241                 ];
12242
12243             }
12244
12245         } else {
12246             
12247 //                Roo.log(" no label && no align");
12248                 cfg = combobox
12249                      
12250                 
12251         }
12252         
12253         var settings=this;
12254         ['xs','sm','md','lg'].map(function(size){
12255             if (settings[size]) {
12256                 cfg.cls += ' col-' + size + '-' + settings[size];
12257             }
12258         });
12259         
12260         return cfg;
12261         
12262     },
12263     
12264     
12265     
12266     // private
12267     onResize : function(w, h){
12268 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12269 //        if(typeof w == 'number'){
12270 //            var x = w - this.trigger.getWidth();
12271 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12272 //            this.trigger.setStyle('left', x+'px');
12273 //        }
12274     },
12275
12276     // private
12277     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12278
12279     // private
12280     getResizeEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     getPositionEl : function(){
12286         return this.inputEl();
12287     },
12288
12289     // private
12290     alignErrorIcon : function(){
12291         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12292     },
12293
12294     // private
12295     initEvents : function(){
12296         
12297         this.createList();
12298         
12299         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12300         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12301         if(!this.multiple && this.showToggleBtn){
12302             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12303             if(this.hideTrigger){
12304                 this.trigger.setDisplayed(false);
12305             }
12306             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12307         }
12308         
12309         if(this.multiple){
12310             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12311         }
12312         
12313         if(this.removable && !this.editable && !this.tickable){
12314             var close = this.closeTriggerEl();
12315             
12316             if(close){
12317                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12318                 close.on('click', this.removeBtnClick, this, close);
12319             }
12320         }
12321         
12322         //this.trigger.addClassOnOver('x-form-trigger-over');
12323         //this.trigger.addClassOnClick('x-form-trigger-click');
12324         
12325         //if(!this.width){
12326         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12327         //}
12328     },
12329     
12330     closeTriggerEl : function()
12331     {
12332         var close = this.el.select('.roo-combo-removable-btn', true).first();
12333         return close ? close : false;
12334     },
12335     
12336     removeBtnClick : function(e, h, el)
12337     {
12338         e.preventDefault();
12339         
12340         if(this.fireEvent("remove", this) !== false){
12341             this.reset();
12342             this.fireEvent("afterremove", this)
12343         }
12344     },
12345     
12346     createList : function()
12347     {
12348         this.list = Roo.get(document.body).createChild({
12349             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12350             cls: 'typeahead typeahead-long dropdown-menu shadow',
12351             style: 'display:none'
12352         });
12353         
12354         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12355         
12356     },
12357
12358     // private
12359     initTrigger : function(){
12360        
12361     },
12362
12363     // private
12364     onDestroy : function(){
12365         if(this.trigger){
12366             this.trigger.removeAllListeners();
12367           //  this.trigger.remove();
12368         }
12369         //if(this.wrap){
12370         //    this.wrap.remove();
12371         //}
12372         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12373     },
12374
12375     // private
12376     onFocus : function(){
12377         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12378         /*
12379         if(!this.mimicing){
12380             this.wrap.addClass('x-trigger-wrap-focus');
12381             this.mimicing = true;
12382             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12383             if(this.monitorTab){
12384                 this.el.on("keydown", this.checkTab, this);
12385             }
12386         }
12387         */
12388     },
12389
12390     // private
12391     checkTab : function(e){
12392         if(e.getKey() == e.TAB){
12393             this.triggerBlur();
12394         }
12395     },
12396
12397     // private
12398     onBlur : function(){
12399         // do nothing
12400     },
12401
12402     // private
12403     mimicBlur : function(e, t){
12404         /*
12405         if(!this.wrap.contains(t) && this.validateBlur()){
12406             this.triggerBlur();
12407         }
12408         */
12409     },
12410
12411     // private
12412     triggerBlur : function(){
12413         this.mimicing = false;
12414         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12415         if(this.monitorTab){
12416             this.el.un("keydown", this.checkTab, this);
12417         }
12418         //this.wrap.removeClass('x-trigger-wrap-focus');
12419         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12420     },
12421
12422     // private
12423     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12424     validateBlur : function(e, t){
12425         return true;
12426     },
12427
12428     // private
12429     onDisable : function(){
12430         this.inputEl().dom.disabled = true;
12431         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12432         //if(this.wrap){
12433         //    this.wrap.addClass('x-item-disabled');
12434         //}
12435     },
12436
12437     // private
12438     onEnable : function(){
12439         this.inputEl().dom.disabled = false;
12440         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12441         //if(this.wrap){
12442         //    this.el.removeClass('x-item-disabled');
12443         //}
12444     },
12445
12446     // private
12447     onShow : function(){
12448         var ae = this.getActionEl();
12449         
12450         if(ae){
12451             ae.dom.style.display = '';
12452             ae.dom.style.visibility = 'visible';
12453         }
12454     },
12455
12456     // private
12457     
12458     onHide : function(){
12459         var ae = this.getActionEl();
12460         ae.dom.style.display = 'none';
12461     },
12462
12463     /**
12464      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12465      * by an implementing function.
12466      * @method
12467      * @param {EventObject} e
12468      */
12469     onTriggerClick : Roo.emptyFn
12470 });
12471  
12472 /*
12473 * Licence: LGPL
12474 */
12475
12476 /**
12477  * @class Roo.bootstrap.CardUploader
12478  * @extends Roo.bootstrap.Button
12479  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12480  * @cfg {Number} errorTimeout default 3000
12481  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12482  * @cfg {Array}  html The button text.
12483
12484  *
12485  * @constructor
12486  * Create a new CardUploader
12487  * @param {Object} config The config object
12488  */
12489
12490 Roo.bootstrap.CardUploader = function(config){
12491     
12492  
12493     
12494     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12495     
12496     
12497     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12498         return r.data.id
12499         });
12500     
12501     
12502 };
12503
12504 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12505     
12506      
12507     errorTimeout : 3000,
12508      
12509     images : false,
12510    
12511     fileCollection : false,
12512     allowBlank : true,
12513     
12514     getAutoCreate : function()
12515     {
12516         
12517         var cfg =  {
12518             cls :'form-group' ,
12519             cn : [
12520                
12521                 {
12522                     tag: 'label',
12523                    //cls : 'input-group-addon',
12524                     html : this.fieldLabel
12525
12526                 },
12527
12528                 {
12529                     tag: 'input',
12530                     type : 'hidden',
12531                     value : this.value,
12532                     cls : 'd-none  form-control'
12533                 },
12534                 
12535                 {
12536                     tag: 'input',
12537                     multiple : 'multiple',
12538                     type : 'file',
12539                     cls : 'd-none  roo-card-upload-selector'
12540                 },
12541                 
12542                 {
12543                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12544                 },
12545                 {
12546                     cls : 'card-columns roo-card-uploader-container'
12547                 }
12548
12549             ]
12550         };
12551            
12552          
12553         return cfg;
12554     },
12555     
12556     getChildContainer : function() /// what children are added to.
12557     {
12558         return this.containerEl;
12559     },
12560    
12561     getButtonContainer : function() /// what children are added to.
12562     {
12563         return this.el.select(".roo-card-uploader-button-container").first();
12564     },
12565    
12566     initEvents : function()
12567     {
12568         
12569         Roo.bootstrap.Input.prototype.initEvents.call(this);
12570         
12571         var t = this;
12572         this.addxtype({
12573             xns: Roo.bootstrap,
12574
12575             xtype : 'Button',
12576             container_method : 'getButtonContainer' ,            
12577             html :  this.html, // fix changable?
12578             cls : 'w-100 ',
12579             listeners : {
12580                 'click' : function(btn, e) {
12581                     t.onClick(e);
12582                 }
12583             }
12584         });
12585         
12586         
12587         
12588         
12589         this.urlAPI = (window.createObjectURL && window) || 
12590                                 (window.URL && URL.revokeObjectURL && URL) || 
12591                                 (window.webkitURL && webkitURL);
12592                         
12593          
12594          
12595          
12596         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12597         
12598         this.selectorEl.on('change', this.onFileSelected, this);
12599         if (this.images) {
12600             var t = this;
12601             this.images.forEach(function(img) {
12602                 t.addCard(img)
12603             });
12604             this.images = false;
12605         }
12606         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12607          
12608        
12609     },
12610     
12611    
12612     onClick : function(e)
12613     {
12614         e.preventDefault();
12615          
12616         this.selectorEl.dom.click();
12617          
12618     },
12619     
12620     onFileSelected : function(e)
12621     {
12622         e.preventDefault();
12623         
12624         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12625             return;
12626         }
12627         
12628         Roo.each(this.selectorEl.dom.files, function(file){    
12629             this.addFile(file);
12630         }, this);
12631          
12632     },
12633     
12634       
12635     
12636       
12637     
12638     addFile : function(file)
12639     {
12640            
12641         if(typeof(file) === 'string'){
12642             throw "Add file by name?"; // should not happen
12643             return;
12644         }
12645         
12646         if(!file || !this.urlAPI){
12647             return;
12648         }
12649         
12650         // file;
12651         // file.type;
12652         
12653         var _this = this;
12654         
12655         
12656         var url = _this.urlAPI.createObjectURL( file);
12657            
12658         this.addCard({
12659             id : Roo.bootstrap.CardUploader.ID--,
12660             is_uploaded : false,
12661             src : url,
12662             title : file.name,
12663             mimetype : file.type,
12664             preview : false,
12665             is_deleted : 0
12666         })
12667         
12668     },
12669     
12670     addCard : function (data)
12671     {
12672         // hidden input element?
12673         // if the file is not an image...
12674         //then we need to use something other that and header_image
12675         var t = this;
12676         //   remove.....
12677         var footer = [
12678             {
12679                 xns : Roo.bootstrap,
12680                 xtype : 'CardFooter',
12681                 items: [
12682                     {
12683                         xns : Roo.bootstrap,
12684                         xtype : 'Element',
12685                         cls : 'd-flex',
12686                         items : [
12687                             
12688                             {
12689                                 xns : Roo.bootstrap,
12690                                 xtype : 'Button',
12691                                 html : String.format("<small>{0}</small>", data.title),
12692                                 cls : 'col-11 text-left',
12693                                 size: 'sm',
12694                                 weight: 'link',
12695                                 fa : 'download',
12696                                 listeners : {
12697                                     click : function() {
12698                                         this.downloadCard(data.id)
12699                                     }
12700                                 }
12701                             },
12702                           
12703                             {
12704                                 xns : Roo.bootstrap,
12705                                 xtype : 'Button',
12706                                 
12707                                 size : 'sm',
12708                                 weight: 'danger',
12709                                 cls : 'col-1',
12710                                 fa : 'times',
12711                                 listeners : {
12712                                     click : function() {
12713                                         t.removeCard(data.id)
12714                                     }
12715                                 }
12716                             }
12717                         ]
12718                     }
12719                     
12720                 ] 
12721             }
12722             
12723         ];
12724
12725         var cn = this.addxtype(
12726             {
12727                  
12728                 xns : Roo.bootstrap,
12729                 xtype : 'Card',
12730                 closeable : true,
12731                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12732                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12733                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12734                 data : data,
12735                 html : false,
12736                  
12737                 items : footer,
12738                 initEvents : function() {
12739                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12740                     this.imgEl = this.el.select('.card-img-top').first();
12741                     if (this.imgEl) {
12742                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12743                         this.imgEl.set({ 'pointer' : 'cursor' });
12744                                   
12745                     }
12746                     
12747                   
12748                 }
12749                 
12750             }
12751         );
12752         // dont' really need ot update items.
12753         // this.items.push(cn);
12754         this.fileCollection.add(cn);
12755         this.updateInput();
12756         
12757     },
12758     removeCard : function(id)
12759     {
12760         
12761         var card  = this.fileCollection.get(id);
12762         card.data.is_deleted = 1;
12763         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12764         this.fileCollection.remove(card);
12765         //this.items = this.items.filter(function(e) { return e != card });
12766         // dont' really need ot update items.
12767         card.el.dom.parentNode.removeChild(card.el.dom);
12768         
12769     },
12770     reset: function()
12771     {
12772         this.fileCollection.each(function(card) {
12773             card.el.dom.parentNode.removeChild(card.el.dom);    
12774         });
12775         this.fileCollection.clear();
12776         this.updateInput();
12777     },
12778     
12779     updateInput : function()
12780     {
12781         var data = [];
12782         this.fileCollection.each(function(e) {
12783             data.push(e.data);
12784         });
12785         
12786         this.inputEl().dom.value = JSON.stringify(data);
12787     }
12788     
12789     
12790 });
12791
12792
12793 Roo.bootstrap.CardUploader.ID = -1;/*
12794  * Based on:
12795  * Ext JS Library 1.1.1
12796  * Copyright(c) 2006-2007, Ext JS, LLC.
12797  *
12798  * Originally Released Under LGPL - original licence link has changed is not relivant.
12799  *
12800  * Fork - LGPL
12801  * <script type="text/javascript">
12802  */
12803
12804
12805 /**
12806  * @class Roo.data.SortTypes
12807  * @singleton
12808  * Defines the default sorting (casting?) comparison functions used when sorting data.
12809  */
12810 Roo.data.SortTypes = {
12811     /**
12812      * Default sort that does nothing
12813      * @param {Mixed} s The value being converted
12814      * @return {Mixed} The comparison value
12815      */
12816     none : function(s){
12817         return s;
12818     },
12819     
12820     /**
12821      * The regular expression used to strip tags
12822      * @type {RegExp}
12823      * @property
12824      */
12825     stripTagsRE : /<\/?[^>]+>/gi,
12826     
12827     /**
12828      * Strips all HTML tags to sort on text only
12829      * @param {Mixed} s The value being converted
12830      * @return {String} The comparison value
12831      */
12832     asText : function(s){
12833         return String(s).replace(this.stripTagsRE, "");
12834     },
12835     
12836     /**
12837      * Strips all HTML tags to sort on text only - Case insensitive
12838      * @param {Mixed} s The value being converted
12839      * @return {String} The comparison value
12840      */
12841     asUCText : function(s){
12842         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12843     },
12844     
12845     /**
12846      * Case insensitive string
12847      * @param {Mixed} s The value being converted
12848      * @return {String} The comparison value
12849      */
12850     asUCString : function(s) {
12851         return String(s).toUpperCase();
12852     },
12853     
12854     /**
12855      * Date sorting
12856      * @param {Mixed} s The value being converted
12857      * @return {Number} The comparison value
12858      */
12859     asDate : function(s) {
12860         if(!s){
12861             return 0;
12862         }
12863         if(s instanceof Date){
12864             return s.getTime();
12865         }
12866         return Date.parse(String(s));
12867     },
12868     
12869     /**
12870      * Float sorting
12871      * @param {Mixed} s The value being converted
12872      * @return {Float} The comparison value
12873      */
12874     asFloat : function(s) {
12875         var val = parseFloat(String(s).replace(/,/g, ""));
12876         if(isNaN(val)) {
12877             val = 0;
12878         }
12879         return val;
12880     },
12881     
12882     /**
12883      * Integer sorting
12884      * @param {Mixed} s The value being converted
12885      * @return {Number} The comparison value
12886      */
12887     asInt : function(s) {
12888         var val = parseInt(String(s).replace(/,/g, ""));
12889         if(isNaN(val)) {
12890             val = 0;
12891         }
12892         return val;
12893     }
12894 };/*
12895  * Based on:
12896  * Ext JS Library 1.1.1
12897  * Copyright(c) 2006-2007, Ext JS, LLC.
12898  *
12899  * Originally Released Under LGPL - original licence link has changed is not relivant.
12900  *
12901  * Fork - LGPL
12902  * <script type="text/javascript">
12903  */
12904
12905 /**
12906 * @class Roo.data.Record
12907  * Instances of this class encapsulate both record <em>definition</em> information, and record
12908  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12909  * to access Records cached in an {@link Roo.data.Store} object.<br>
12910  * <p>
12911  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12912  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12913  * objects.<br>
12914  * <p>
12915  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12916  * @constructor
12917  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12918  * {@link #create}. The parameters are the same.
12919  * @param {Array} data An associative Array of data values keyed by the field name.
12920  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12921  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12922  * not specified an integer id is generated.
12923  */
12924 Roo.data.Record = function(data, id){
12925     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12926     this.data = data;
12927 };
12928
12929 /**
12930  * Generate a constructor for a specific record layout.
12931  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12932  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12933  * Each field definition object may contain the following properties: <ul>
12934  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
12935  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12936  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12937  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12938  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12939  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12940  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12941  * this may be omitted.</p></li>
12942  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12943  * <ul><li>auto (Default, implies no conversion)</li>
12944  * <li>string</li>
12945  * <li>int</li>
12946  * <li>float</li>
12947  * <li>boolean</li>
12948  * <li>date</li></ul></p></li>
12949  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12950  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12951  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12952  * by the Reader into an object that will be stored in the Record. It is passed the
12953  * following parameters:<ul>
12954  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12955  * </ul></p></li>
12956  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12957  * </ul>
12958  * <br>usage:<br><pre><code>
12959 var TopicRecord = Roo.data.Record.create(
12960     {name: 'title', mapping: 'topic_title'},
12961     {name: 'author', mapping: 'username'},
12962     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12963     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12964     {name: 'lastPoster', mapping: 'user2'},
12965     {name: 'excerpt', mapping: 'post_text'}
12966 );
12967
12968 var myNewRecord = new TopicRecord({
12969     title: 'Do my job please',
12970     author: 'noobie',
12971     totalPosts: 1,
12972     lastPost: new Date(),
12973     lastPoster: 'Animal',
12974     excerpt: 'No way dude!'
12975 });
12976 myStore.add(myNewRecord);
12977 </code></pre>
12978  * @method create
12979  * @static
12980  */
12981 Roo.data.Record.create = function(o){
12982     var f = function(){
12983         f.superclass.constructor.apply(this, arguments);
12984     };
12985     Roo.extend(f, Roo.data.Record);
12986     var p = f.prototype;
12987     p.fields = new Roo.util.MixedCollection(false, function(field){
12988         return field.name;
12989     });
12990     for(var i = 0, len = o.length; i < len; i++){
12991         p.fields.add(new Roo.data.Field(o[i]));
12992     }
12993     f.getField = function(name){
12994         return p.fields.get(name);  
12995     };
12996     return f;
12997 };
12998
12999 Roo.data.Record.AUTO_ID = 1000;
13000 Roo.data.Record.EDIT = 'edit';
13001 Roo.data.Record.REJECT = 'reject';
13002 Roo.data.Record.COMMIT = 'commit';
13003
13004 Roo.data.Record.prototype = {
13005     /**
13006      * Readonly flag - true if this record has been modified.
13007      * @type Boolean
13008      */
13009     dirty : false,
13010     editing : false,
13011     error: null,
13012     modified: null,
13013
13014     // private
13015     join : function(store){
13016         this.store = store;
13017     },
13018
13019     /**
13020      * Set the named field to the specified value.
13021      * @param {String} name The name of the field to set.
13022      * @param {Object} value The value to set the field to.
13023      */
13024     set : function(name, value){
13025         if(this.data[name] == value){
13026             return;
13027         }
13028         this.dirty = true;
13029         if(!this.modified){
13030             this.modified = {};
13031         }
13032         if(typeof this.modified[name] == 'undefined'){
13033             this.modified[name] = this.data[name];
13034         }
13035         this.data[name] = value;
13036         if(!this.editing && this.store){
13037             this.store.afterEdit(this);
13038         }       
13039     },
13040
13041     /**
13042      * Get the value of the named field.
13043      * @param {String} name The name of the field to get the value of.
13044      * @return {Object} The value of the field.
13045      */
13046     get : function(name){
13047         return this.data[name]; 
13048     },
13049
13050     // private
13051     beginEdit : function(){
13052         this.editing = true;
13053         this.modified = {}; 
13054     },
13055
13056     // private
13057     cancelEdit : function(){
13058         this.editing = false;
13059         delete this.modified;
13060     },
13061
13062     // private
13063     endEdit : function(){
13064         this.editing = false;
13065         if(this.dirty && this.store){
13066             this.store.afterEdit(this);
13067         }
13068     },
13069
13070     /**
13071      * Usually called by the {@link Roo.data.Store} which owns the Record.
13072      * Rejects all changes made to the Record since either creation, or the last commit operation.
13073      * Modified fields are reverted to their original values.
13074      * <p>
13075      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13076      * of reject operations.
13077      */
13078     reject : function(){
13079         var m = this.modified;
13080         for(var n in m){
13081             if(typeof m[n] != "function"){
13082                 this.data[n] = m[n];
13083             }
13084         }
13085         this.dirty = false;
13086         delete this.modified;
13087         this.editing = false;
13088         if(this.store){
13089             this.store.afterReject(this);
13090         }
13091     },
13092
13093     /**
13094      * Usually called by the {@link Roo.data.Store} which owns the Record.
13095      * Commits all changes made to the Record since either creation, or the last commit operation.
13096      * <p>
13097      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13098      * of commit operations.
13099      */
13100     commit : function(){
13101         this.dirty = false;
13102         delete this.modified;
13103         this.editing = false;
13104         if(this.store){
13105             this.store.afterCommit(this);
13106         }
13107     },
13108
13109     // private
13110     hasError : function(){
13111         return this.error != null;
13112     },
13113
13114     // private
13115     clearError : function(){
13116         this.error = null;
13117     },
13118
13119     /**
13120      * Creates a copy of this record.
13121      * @param {String} id (optional) A new record id if you don't want to use this record's id
13122      * @return {Record}
13123      */
13124     copy : function(newId) {
13125         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13126     }
13127 };/*
13128  * Based on:
13129  * Ext JS Library 1.1.1
13130  * Copyright(c) 2006-2007, Ext JS, LLC.
13131  *
13132  * Originally Released Under LGPL - original licence link has changed is not relivant.
13133  *
13134  * Fork - LGPL
13135  * <script type="text/javascript">
13136  */
13137
13138
13139
13140 /**
13141  * @class Roo.data.Store
13142  * @extends Roo.util.Observable
13143  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13144  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13145  * <p>
13146  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
13147  * has no knowledge of the format of the data returned by the Proxy.<br>
13148  * <p>
13149  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13150  * instances from the data object. These records are cached and made available through accessor functions.
13151  * @constructor
13152  * Creates a new Store.
13153  * @param {Object} config A config object containing the objects needed for the Store to access data,
13154  * and read the data into Records.
13155  */
13156 Roo.data.Store = function(config){
13157     this.data = new Roo.util.MixedCollection(false);
13158     this.data.getKey = function(o){
13159         return o.id;
13160     };
13161     this.baseParams = {};
13162     // private
13163     this.paramNames = {
13164         "start" : "start",
13165         "limit" : "limit",
13166         "sort" : "sort",
13167         "dir" : "dir",
13168         "multisort" : "_multisort"
13169     };
13170
13171     if(config && config.data){
13172         this.inlineData = config.data;
13173         delete config.data;
13174     }
13175
13176     Roo.apply(this, config);
13177     
13178     if(this.reader){ // reader passed
13179         this.reader = Roo.factory(this.reader, Roo.data);
13180         this.reader.xmodule = this.xmodule || false;
13181         if(!this.recordType){
13182             this.recordType = this.reader.recordType;
13183         }
13184         if(this.reader.onMetaChange){
13185             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13186         }
13187     }
13188
13189     if(this.recordType){
13190         this.fields = this.recordType.prototype.fields;
13191     }
13192     this.modified = [];
13193
13194     this.addEvents({
13195         /**
13196          * @event datachanged
13197          * Fires when the data cache has changed, and a widget which is using this Store
13198          * as a Record cache should refresh its view.
13199          * @param {Store} this
13200          */
13201         datachanged : true,
13202         /**
13203          * @event metachange
13204          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13205          * @param {Store} this
13206          * @param {Object} meta The JSON metadata
13207          */
13208         metachange : true,
13209         /**
13210          * @event add
13211          * Fires when Records have been added to the Store
13212          * @param {Store} this
13213          * @param {Roo.data.Record[]} records The array of Records added
13214          * @param {Number} index The index at which the record(s) were added
13215          */
13216         add : true,
13217         /**
13218          * @event remove
13219          * Fires when a Record has been removed from the Store
13220          * @param {Store} this
13221          * @param {Roo.data.Record} record The Record that was removed
13222          * @param {Number} index The index at which the record was removed
13223          */
13224         remove : true,
13225         /**
13226          * @event update
13227          * Fires when a Record has been updated
13228          * @param {Store} this
13229          * @param {Roo.data.Record} record The Record that was updated
13230          * @param {String} operation The update operation being performed.  Value may be one of:
13231          * <pre><code>
13232  Roo.data.Record.EDIT
13233  Roo.data.Record.REJECT
13234  Roo.data.Record.COMMIT
13235          * </code></pre>
13236          */
13237         update : true,
13238         /**
13239          * @event clear
13240          * Fires when the data cache has been cleared.
13241          * @param {Store} this
13242          */
13243         clear : true,
13244         /**
13245          * @event beforeload
13246          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13247          * the load action will be canceled.
13248          * @param {Store} this
13249          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13250          */
13251         beforeload : true,
13252         /**
13253          * @event beforeloadadd
13254          * Fires after a new set of Records has been loaded.
13255          * @param {Store} this
13256          * @param {Roo.data.Record[]} records The Records that were loaded
13257          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13258          */
13259         beforeloadadd : true,
13260         /**
13261          * @event load
13262          * Fires after a new set of Records has been loaded, before they are added to the store.
13263          * @param {Store} this
13264          * @param {Roo.data.Record[]} records The Records that were loaded
13265          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13266          * @params {Object} return from reader
13267          */
13268         load : true,
13269         /**
13270          * @event loadexception
13271          * Fires if an exception occurs in the Proxy during loading.
13272          * Called with the signature of the Proxy's "loadexception" event.
13273          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13274          * 
13275          * @param {Proxy} 
13276          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13277          * @param {Object} load options 
13278          * @param {Object} jsonData from your request (normally this contains the Exception)
13279          */
13280         loadexception : true
13281     });
13282     
13283     if(this.proxy){
13284         this.proxy = Roo.factory(this.proxy, Roo.data);
13285         this.proxy.xmodule = this.xmodule || false;
13286         this.relayEvents(this.proxy,  ["loadexception"]);
13287     }
13288     this.sortToggle = {};
13289     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13290
13291     Roo.data.Store.superclass.constructor.call(this);
13292
13293     if(this.inlineData){
13294         this.loadData(this.inlineData);
13295         delete this.inlineData;
13296     }
13297 };
13298
13299 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13300      /**
13301     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13302     * without a remote query - used by combo/forms at present.
13303     */
13304     
13305     /**
13306     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13307     */
13308     /**
13309     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13310     */
13311     /**
13312     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13313     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13314     */
13315     /**
13316     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13317     * on any HTTP request
13318     */
13319     /**
13320     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13321     */
13322     /**
13323     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13324     */
13325     multiSort: false,
13326     /**
13327     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13328     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13329     */
13330     remoteSort : false,
13331
13332     /**
13333     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13334      * loaded or when a record is removed. (defaults to false).
13335     */
13336     pruneModifiedRecords : false,
13337
13338     // private
13339     lastOptions : null,
13340
13341     /**
13342      * Add Records to the Store and fires the add event.
13343      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13344      */
13345     add : function(records){
13346         records = [].concat(records);
13347         for(var i = 0, len = records.length; i < len; i++){
13348             records[i].join(this);
13349         }
13350         var index = this.data.length;
13351         this.data.addAll(records);
13352         this.fireEvent("add", this, records, index);
13353     },
13354
13355     /**
13356      * Remove a Record from the Store and fires the remove event.
13357      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13358      */
13359     remove : function(record){
13360         var index = this.data.indexOf(record);
13361         this.data.removeAt(index);
13362  
13363         if(this.pruneModifiedRecords){
13364             this.modified.remove(record);
13365         }
13366         this.fireEvent("remove", this, record, index);
13367     },
13368
13369     /**
13370      * Remove all Records from the Store and fires the clear event.
13371      */
13372     removeAll : function(){
13373         this.data.clear();
13374         if(this.pruneModifiedRecords){
13375             this.modified = [];
13376         }
13377         this.fireEvent("clear", this);
13378     },
13379
13380     /**
13381      * Inserts Records to the Store at the given index and fires the add event.
13382      * @param {Number} index The start index at which to insert the passed Records.
13383      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13384      */
13385     insert : function(index, records){
13386         records = [].concat(records);
13387         for(var i = 0, len = records.length; i < len; i++){
13388             this.data.insert(index, records[i]);
13389             records[i].join(this);
13390         }
13391         this.fireEvent("add", this, records, index);
13392     },
13393
13394     /**
13395      * Get the index within the cache of the passed Record.
13396      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13397      * @return {Number} The index of the passed Record. Returns -1 if not found.
13398      */
13399     indexOf : function(record){
13400         return this.data.indexOf(record);
13401     },
13402
13403     /**
13404      * Get the index within the cache of the Record with the passed id.
13405      * @param {String} id The id of the Record to find.
13406      * @return {Number} The index of the Record. Returns -1 if not found.
13407      */
13408     indexOfId : function(id){
13409         return this.data.indexOfKey(id);
13410     },
13411
13412     /**
13413      * Get the Record with the specified id.
13414      * @param {String} id The id of the Record to find.
13415      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13416      */
13417     getById : function(id){
13418         return this.data.key(id);
13419     },
13420
13421     /**
13422      * Get the Record at the specified index.
13423      * @param {Number} index The index of the Record to find.
13424      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13425      */
13426     getAt : function(index){
13427         return this.data.itemAt(index);
13428     },
13429
13430     /**
13431      * Returns a range of Records between specified indices.
13432      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13433      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13434      * @return {Roo.data.Record[]} An array of Records
13435      */
13436     getRange : function(start, end){
13437         return this.data.getRange(start, end);
13438     },
13439
13440     // private
13441     storeOptions : function(o){
13442         o = Roo.apply({}, o);
13443         delete o.callback;
13444         delete o.scope;
13445         this.lastOptions = o;
13446     },
13447
13448     /**
13449      * Loads the Record cache from the configured Proxy using the configured Reader.
13450      * <p>
13451      * If using remote paging, then the first load call must specify the <em>start</em>
13452      * and <em>limit</em> properties in the options.params property to establish the initial
13453      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13454      * <p>
13455      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13456      * and this call will return before the new data has been loaded. Perform any post-processing
13457      * in a callback function, or in a "load" event handler.</strong>
13458      * <p>
13459      * @param {Object} options An object containing properties which control loading options:<ul>
13460      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13461      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13462      * passed the following arguments:<ul>
13463      * <li>r : Roo.data.Record[]</li>
13464      * <li>options: Options object from the load call</li>
13465      * <li>success: Boolean success indicator</li></ul></li>
13466      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13467      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13468      * </ul>
13469      */
13470     load : function(options){
13471         options = options || {};
13472         if(this.fireEvent("beforeload", this, options) !== false){
13473             this.storeOptions(options);
13474             var p = Roo.apply(options.params || {}, this.baseParams);
13475             // if meta was not loaded from remote source.. try requesting it.
13476             if (!this.reader.metaFromRemote) {
13477                 p._requestMeta = 1;
13478             }
13479             if(this.sortInfo && this.remoteSort){
13480                 var pn = this.paramNames;
13481                 p[pn["sort"]] = this.sortInfo.field;
13482                 p[pn["dir"]] = this.sortInfo.direction;
13483             }
13484             if (this.multiSort) {
13485                 var pn = this.paramNames;
13486                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13487             }
13488             
13489             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13490         }
13491     },
13492
13493     /**
13494      * Reloads the Record cache from the configured Proxy using the configured Reader and
13495      * the options from the last load operation performed.
13496      * @param {Object} options (optional) An object containing properties which may override the options
13497      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13498      * the most recently used options are reused).
13499      */
13500     reload : function(options){
13501         this.load(Roo.applyIf(options||{}, this.lastOptions));
13502     },
13503
13504     // private
13505     // Called as a callback by the Reader during a load operation.
13506     loadRecords : function(o, options, success){
13507         if(!o || success === false){
13508             if(success !== false){
13509                 this.fireEvent("load", this, [], options, o);
13510             }
13511             if(options.callback){
13512                 options.callback.call(options.scope || this, [], options, false);
13513             }
13514             return;
13515         }
13516         // if data returned failure - throw an exception.
13517         if (o.success === false) {
13518             // show a message if no listener is registered.
13519             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13520                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13521             }
13522             // loadmask wil be hooked into this..
13523             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13524             return;
13525         }
13526         var r = o.records, t = o.totalRecords || r.length;
13527         
13528         this.fireEvent("beforeloadadd", this, r, options, o);
13529         
13530         if(!options || options.add !== true){
13531             if(this.pruneModifiedRecords){
13532                 this.modified = [];
13533             }
13534             for(var i = 0, len = r.length; i < len; i++){
13535                 r[i].join(this);
13536             }
13537             if(this.snapshot){
13538                 this.data = this.snapshot;
13539                 delete this.snapshot;
13540             }
13541             this.data.clear();
13542             this.data.addAll(r);
13543             this.totalLength = t;
13544             this.applySort();
13545             this.fireEvent("datachanged", this);
13546         }else{
13547             this.totalLength = Math.max(t, this.data.length+r.length);
13548             this.add(r);
13549         }
13550         
13551         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13552                 
13553             var e = new Roo.data.Record({});
13554
13555             e.set(this.parent.displayField, this.parent.emptyTitle);
13556             e.set(this.parent.valueField, '');
13557
13558             this.insert(0, e);
13559         }
13560             
13561         this.fireEvent("load", this, r, options, o);
13562         if(options.callback){
13563             options.callback.call(options.scope || this, r, options, true);
13564         }
13565     },
13566
13567
13568     /**
13569      * Loads data from a passed data block. A Reader which understands the format of the data
13570      * must have been configured in the constructor.
13571      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13572      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13573      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13574      */
13575     loadData : function(o, append){
13576         var r = this.reader.readRecords(o);
13577         this.loadRecords(r, {add: append}, true);
13578     },
13579     
13580      /**
13581      * using 'cn' the nested child reader read the child array into it's child stores.
13582      * @param {Object} rec The record with a 'children array
13583      */
13584     loadDataFromChildren : function(rec)
13585     {
13586         this.loadData(this.reader.toLoadData(rec));
13587     },
13588     
13589
13590     /**
13591      * Gets the number of cached records.
13592      * <p>
13593      * <em>If using paging, this may not be the total size of the dataset. If the data object
13594      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13595      * the data set size</em>
13596      */
13597     getCount : function(){
13598         return this.data.length || 0;
13599     },
13600
13601     /**
13602      * Gets the total number of records in the dataset as returned by the server.
13603      * <p>
13604      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13605      * the dataset size</em>
13606      */
13607     getTotalCount : function(){
13608         return this.totalLength || 0;
13609     },
13610
13611     /**
13612      * Returns the sort state of the Store as an object with two properties:
13613      * <pre><code>
13614  field {String} The name of the field by which the Records are sorted
13615  direction {String} The sort order, "ASC" or "DESC"
13616      * </code></pre>
13617      */
13618     getSortState : function(){
13619         return this.sortInfo;
13620     },
13621
13622     // private
13623     applySort : function(){
13624         if(this.sortInfo && !this.remoteSort){
13625             var s = this.sortInfo, f = s.field;
13626             var st = this.fields.get(f).sortType;
13627             var fn = function(r1, r2){
13628                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13629                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13630             };
13631             this.data.sort(s.direction, fn);
13632             if(this.snapshot && this.snapshot != this.data){
13633                 this.snapshot.sort(s.direction, fn);
13634             }
13635         }
13636     },
13637
13638     /**
13639      * Sets the default sort column and order to be used by the next load operation.
13640      * @param {String} fieldName The name of the field to sort by.
13641      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13642      */
13643     setDefaultSort : function(field, dir){
13644         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13645     },
13646
13647     /**
13648      * Sort the Records.
13649      * If remote sorting is used, the sort is performed on the server, and the cache is
13650      * reloaded. If local sorting is used, the cache is sorted internally.
13651      * @param {String} fieldName The name of the field to sort by.
13652      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13653      */
13654     sort : function(fieldName, dir){
13655         var f = this.fields.get(fieldName);
13656         if(!dir){
13657             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13658             
13659             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13660                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13661             }else{
13662                 dir = f.sortDir;
13663             }
13664         }
13665         this.sortToggle[f.name] = dir;
13666         this.sortInfo = {field: f.name, direction: dir};
13667         if(!this.remoteSort){
13668             this.applySort();
13669             this.fireEvent("datachanged", this);
13670         }else{
13671             this.load(this.lastOptions);
13672         }
13673     },
13674
13675     /**
13676      * Calls the specified function for each of the Records in the cache.
13677      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13678      * Returning <em>false</em> aborts and exits the iteration.
13679      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13680      */
13681     each : function(fn, scope){
13682         this.data.each(fn, scope);
13683     },
13684
13685     /**
13686      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13687      * (e.g., during paging).
13688      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13689      */
13690     getModifiedRecords : function(){
13691         return this.modified;
13692     },
13693
13694     // private
13695     createFilterFn : function(property, value, anyMatch){
13696         if(!value.exec){ // not a regex
13697             value = String(value);
13698             if(value.length == 0){
13699                 return false;
13700             }
13701             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13702         }
13703         return function(r){
13704             return value.test(r.data[property]);
13705         };
13706     },
13707
13708     /**
13709      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13710      * @param {String} property A field on your records
13711      * @param {Number} start The record index to start at (defaults to 0)
13712      * @param {Number} end The last record index to include (defaults to length - 1)
13713      * @return {Number} The sum
13714      */
13715     sum : function(property, start, end){
13716         var rs = this.data.items, v = 0;
13717         start = start || 0;
13718         end = (end || end === 0) ? end : rs.length-1;
13719
13720         for(var i = start; i <= end; i++){
13721             v += (rs[i].data[property] || 0);
13722         }
13723         return v;
13724     },
13725
13726     /**
13727      * Filter the records by a specified property.
13728      * @param {String} field A field on your records
13729      * @param {String/RegExp} value Either a string that the field
13730      * should start with or a RegExp to test against the field
13731      * @param {Boolean} anyMatch True to match any part not just the beginning
13732      */
13733     filter : function(property, value, anyMatch){
13734         var fn = this.createFilterFn(property, value, anyMatch);
13735         return fn ? this.filterBy(fn) : this.clearFilter();
13736     },
13737
13738     /**
13739      * Filter by a function. The specified function will be called with each
13740      * record in this data source. If the function returns true the record is included,
13741      * otherwise it is filtered.
13742      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13743      * @param {Object} scope (optional) The scope of the function (defaults to this)
13744      */
13745     filterBy : function(fn, scope){
13746         this.snapshot = this.snapshot || this.data;
13747         this.data = this.queryBy(fn, scope||this);
13748         this.fireEvent("datachanged", this);
13749     },
13750
13751     /**
13752      * Query the records by a specified property.
13753      * @param {String} field A field on your records
13754      * @param {String/RegExp} value Either a string that the field
13755      * should start with or a RegExp to test against the field
13756      * @param {Boolean} anyMatch True to match any part not just the beginning
13757      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13758      */
13759     query : function(property, value, anyMatch){
13760         var fn = this.createFilterFn(property, value, anyMatch);
13761         return fn ? this.queryBy(fn) : this.data.clone();
13762     },
13763
13764     /**
13765      * Query by a function. The specified function will be called with each
13766      * record in this data source. If the function returns true the record is included
13767      * in the results.
13768      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13769      * @param {Object} scope (optional) The scope of the function (defaults to this)
13770       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13771      **/
13772     queryBy : function(fn, scope){
13773         var data = this.snapshot || this.data;
13774         return data.filterBy(fn, scope||this);
13775     },
13776
13777     /**
13778      * Collects unique values for a particular dataIndex from this store.
13779      * @param {String} dataIndex The property to collect
13780      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13781      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13782      * @return {Array} An array of the unique values
13783      **/
13784     collect : function(dataIndex, allowNull, bypassFilter){
13785         var d = (bypassFilter === true && this.snapshot) ?
13786                 this.snapshot.items : this.data.items;
13787         var v, sv, r = [], l = {};
13788         for(var i = 0, len = d.length; i < len; i++){
13789             v = d[i].data[dataIndex];
13790             sv = String(v);
13791             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13792                 l[sv] = true;
13793                 r[r.length] = v;
13794             }
13795         }
13796         return r;
13797     },
13798
13799     /**
13800      * Revert to a view of the Record cache with no filtering applied.
13801      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13802      */
13803     clearFilter : function(suppressEvent){
13804         if(this.snapshot && this.snapshot != this.data){
13805             this.data = this.snapshot;
13806             delete this.snapshot;
13807             if(suppressEvent !== true){
13808                 this.fireEvent("datachanged", this);
13809             }
13810         }
13811     },
13812
13813     // private
13814     afterEdit : function(record){
13815         if(this.modified.indexOf(record) == -1){
13816             this.modified.push(record);
13817         }
13818         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13819     },
13820     
13821     // private
13822     afterReject : function(record){
13823         this.modified.remove(record);
13824         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13825     },
13826
13827     // private
13828     afterCommit : function(record){
13829         this.modified.remove(record);
13830         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13831     },
13832
13833     /**
13834      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13835      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13836      */
13837     commitChanges : function(){
13838         var m = this.modified.slice(0);
13839         this.modified = [];
13840         for(var i = 0, len = m.length; i < len; i++){
13841             m[i].commit();
13842         }
13843     },
13844
13845     /**
13846      * Cancel outstanding changes on all changed records.
13847      */
13848     rejectChanges : function(){
13849         var m = this.modified.slice(0);
13850         this.modified = [];
13851         for(var i = 0, len = m.length; i < len; i++){
13852             m[i].reject();
13853         }
13854     },
13855
13856     onMetaChange : function(meta, rtype, o){
13857         this.recordType = rtype;
13858         this.fields = rtype.prototype.fields;
13859         delete this.snapshot;
13860         this.sortInfo = meta.sortInfo || this.sortInfo;
13861         this.modified = [];
13862         this.fireEvent('metachange', this, this.reader.meta);
13863     },
13864     
13865     moveIndex : function(data, type)
13866     {
13867         var index = this.indexOf(data);
13868         
13869         var newIndex = index + type;
13870         
13871         this.remove(data);
13872         
13873         this.insert(newIndex, data);
13874         
13875     }
13876 });/*
13877  * Based on:
13878  * Ext JS Library 1.1.1
13879  * Copyright(c) 2006-2007, Ext JS, LLC.
13880  *
13881  * Originally Released Under LGPL - original licence link has changed is not relivant.
13882  *
13883  * Fork - LGPL
13884  * <script type="text/javascript">
13885  */
13886
13887 /**
13888  * @class Roo.data.SimpleStore
13889  * @extends Roo.data.Store
13890  * Small helper class to make creating Stores from Array data easier.
13891  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13892  * @cfg {Array} fields An array of field definition objects, or field name strings.
13893  * @cfg {Object} an existing reader (eg. copied from another store)
13894  * @cfg {Array} data The multi-dimensional array of data
13895  * @constructor
13896  * @param {Object} config
13897  */
13898 Roo.data.SimpleStore = function(config)
13899 {
13900     Roo.data.SimpleStore.superclass.constructor.call(this, {
13901         isLocal : true,
13902         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13903                 id: config.id
13904             },
13905             Roo.data.Record.create(config.fields)
13906         ),
13907         proxy : new Roo.data.MemoryProxy(config.data)
13908     });
13909     this.load();
13910 };
13911 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13912  * Based on:
13913  * Ext JS Library 1.1.1
13914  * Copyright(c) 2006-2007, Ext JS, LLC.
13915  *
13916  * Originally Released Under LGPL - original licence link has changed is not relivant.
13917  *
13918  * Fork - LGPL
13919  * <script type="text/javascript">
13920  */
13921
13922 /**
13923 /**
13924  * @extends Roo.data.Store
13925  * @class Roo.data.JsonStore
13926  * Small helper class to make creating Stores for JSON data easier. <br/>
13927 <pre><code>
13928 var store = new Roo.data.JsonStore({
13929     url: 'get-images.php',
13930     root: 'images',
13931     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13932 });
13933 </code></pre>
13934  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13935  * JsonReader and HttpProxy (unless inline data is provided).</b>
13936  * @cfg {Array} fields An array of field definition objects, or field name strings.
13937  * @constructor
13938  * @param {Object} config
13939  */
13940 Roo.data.JsonStore = function(c){
13941     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13942         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13943         reader: new Roo.data.JsonReader(c, c.fields)
13944     }));
13945 };
13946 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13947  * Based on:
13948  * Ext JS Library 1.1.1
13949  * Copyright(c) 2006-2007, Ext JS, LLC.
13950  *
13951  * Originally Released Under LGPL - original licence link has changed is not relivant.
13952  *
13953  * Fork - LGPL
13954  * <script type="text/javascript">
13955  */
13956
13957  
13958 Roo.data.Field = function(config){
13959     if(typeof config == "string"){
13960         config = {name: config};
13961     }
13962     Roo.apply(this, config);
13963     
13964     if(!this.type){
13965         this.type = "auto";
13966     }
13967     
13968     var st = Roo.data.SortTypes;
13969     // named sortTypes are supported, here we look them up
13970     if(typeof this.sortType == "string"){
13971         this.sortType = st[this.sortType];
13972     }
13973     
13974     // set default sortType for strings and dates
13975     if(!this.sortType){
13976         switch(this.type){
13977             case "string":
13978                 this.sortType = st.asUCString;
13979                 break;
13980             case "date":
13981                 this.sortType = st.asDate;
13982                 break;
13983             default:
13984                 this.sortType = st.none;
13985         }
13986     }
13987
13988     // define once
13989     var stripRe = /[\$,%]/g;
13990
13991     // prebuilt conversion function for this field, instead of
13992     // switching every time we're reading a value
13993     if(!this.convert){
13994         var cv, dateFormat = this.dateFormat;
13995         switch(this.type){
13996             case "":
13997             case "auto":
13998             case undefined:
13999                 cv = function(v){ return v; };
14000                 break;
14001             case "string":
14002                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14003                 break;
14004             case "int":
14005                 cv = function(v){
14006                     return v !== undefined && v !== null && v !== '' ?
14007                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14008                     };
14009                 break;
14010             case "float":
14011                 cv = function(v){
14012                     return v !== undefined && v !== null && v !== '' ?
14013                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14014                     };
14015                 break;
14016             case "bool":
14017             case "boolean":
14018                 cv = function(v){ return v === true || v === "true" || v == 1; };
14019                 break;
14020             case "date":
14021                 cv = function(v){
14022                     if(!v){
14023                         return '';
14024                     }
14025                     if(v instanceof Date){
14026                         return v;
14027                     }
14028                     if(dateFormat){
14029                         if(dateFormat == "timestamp"){
14030                             return new Date(v*1000);
14031                         }
14032                         return Date.parseDate(v, dateFormat);
14033                     }
14034                     var parsed = Date.parse(v);
14035                     return parsed ? new Date(parsed) : null;
14036                 };
14037              break;
14038             
14039         }
14040         this.convert = cv;
14041     }
14042 };
14043
14044 Roo.data.Field.prototype = {
14045     dateFormat: null,
14046     defaultValue: "",
14047     mapping: null,
14048     sortType : null,
14049     sortDir : "ASC"
14050 };/*
14051  * Based on:
14052  * Ext JS Library 1.1.1
14053  * Copyright(c) 2006-2007, Ext JS, LLC.
14054  *
14055  * Originally Released Under LGPL - original licence link has changed is not relivant.
14056  *
14057  * Fork - LGPL
14058  * <script type="text/javascript">
14059  */
14060  
14061 // Base class for reading structured data from a data source.  This class is intended to be
14062 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14063
14064 /**
14065  * @class Roo.data.DataReader
14066  * Base class for reading structured data from a data source.  This class is intended to be
14067  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14068  */
14069
14070 Roo.data.DataReader = function(meta, recordType){
14071     
14072     this.meta = meta;
14073     
14074     this.recordType = recordType instanceof Array ? 
14075         Roo.data.Record.create(recordType) : recordType;
14076 };
14077
14078 Roo.data.DataReader.prototype = {
14079     
14080     
14081     readerType : 'Data',
14082      /**
14083      * Create an empty record
14084      * @param {Object} data (optional) - overlay some values
14085      * @return {Roo.data.Record} record created.
14086      */
14087     newRow :  function(d) {
14088         var da =  {};
14089         this.recordType.prototype.fields.each(function(c) {
14090             switch( c.type) {
14091                 case 'int' : da[c.name] = 0; break;
14092                 case 'date' : da[c.name] = new Date(); break;
14093                 case 'float' : da[c.name] = 0.0; break;
14094                 case 'boolean' : da[c.name] = false; break;
14095                 default : da[c.name] = ""; break;
14096             }
14097             
14098         });
14099         return new this.recordType(Roo.apply(da, d));
14100     }
14101     
14102     
14103 };/*
14104  * Based on:
14105  * Ext JS Library 1.1.1
14106  * Copyright(c) 2006-2007, Ext JS, LLC.
14107  *
14108  * Originally Released Under LGPL - original licence link has changed is not relivant.
14109  *
14110  * Fork - LGPL
14111  * <script type="text/javascript">
14112  */
14113
14114 /**
14115  * @class Roo.data.DataProxy
14116  * @extends Roo.data.Observable
14117  * This class is an abstract base class for implementations which provide retrieval of
14118  * unformatted data objects.<br>
14119  * <p>
14120  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14121  * (of the appropriate type which knows how to parse the data object) to provide a block of
14122  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14123  * <p>
14124  * Custom implementations must implement the load method as described in
14125  * {@link Roo.data.HttpProxy#load}.
14126  */
14127 Roo.data.DataProxy = function(){
14128     this.addEvents({
14129         /**
14130          * @event beforeload
14131          * Fires before a network request is made to retrieve a data object.
14132          * @param {Object} This DataProxy object.
14133          * @param {Object} params The params parameter to the load function.
14134          */
14135         beforeload : true,
14136         /**
14137          * @event load
14138          * Fires before the load method's callback is called.
14139          * @param {Object} This DataProxy object.
14140          * @param {Object} o The data object.
14141          * @param {Object} arg The callback argument object passed to the load function.
14142          */
14143         load : true,
14144         /**
14145          * @event loadexception
14146          * Fires if an Exception occurs during data retrieval.
14147          * @param {Object} This DataProxy object.
14148          * @param {Object} o The data object.
14149          * @param {Object} arg The callback argument object passed to the load function.
14150          * @param {Object} e The Exception.
14151          */
14152         loadexception : true
14153     });
14154     Roo.data.DataProxy.superclass.constructor.call(this);
14155 };
14156
14157 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14158
14159     /**
14160      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14161      */
14162 /*
14163  * Based on:
14164  * Ext JS Library 1.1.1
14165  * Copyright(c) 2006-2007, Ext JS, LLC.
14166  *
14167  * Originally Released Under LGPL - original licence link has changed is not relivant.
14168  *
14169  * Fork - LGPL
14170  * <script type="text/javascript">
14171  */
14172 /**
14173  * @class Roo.data.MemoryProxy
14174  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14175  * to the Reader when its load method is called.
14176  * @constructor
14177  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14178  */
14179 Roo.data.MemoryProxy = function(data){
14180     if (data.data) {
14181         data = data.data;
14182     }
14183     Roo.data.MemoryProxy.superclass.constructor.call(this);
14184     this.data = data;
14185 };
14186
14187 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14188     
14189     /**
14190      * Load data from the requested source (in this case an in-memory
14191      * data object passed to the constructor), read the data object into
14192      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14193      * process that block using the passed callback.
14194      * @param {Object} params This parameter is not used by the MemoryProxy class.
14195      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14196      * object into a block of Roo.data.Records.
14197      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14198      * The function must be passed <ul>
14199      * <li>The Record block object</li>
14200      * <li>The "arg" argument from the load function</li>
14201      * <li>A boolean success indicator</li>
14202      * </ul>
14203      * @param {Object} scope The scope in which to call the callback
14204      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14205      */
14206     load : function(params, reader, callback, scope, arg){
14207         params = params || {};
14208         var result;
14209         try {
14210             result = reader.readRecords(params.data ? params.data :this.data);
14211         }catch(e){
14212             this.fireEvent("loadexception", this, arg, null, e);
14213             callback.call(scope, null, arg, false);
14214             return;
14215         }
14216         callback.call(scope, result, arg, true);
14217     },
14218     
14219     // private
14220     update : function(params, records){
14221         
14222     }
14223 });/*
14224  * Based on:
14225  * Ext JS Library 1.1.1
14226  * Copyright(c) 2006-2007, Ext JS, LLC.
14227  *
14228  * Originally Released Under LGPL - original licence link has changed is not relivant.
14229  *
14230  * Fork - LGPL
14231  * <script type="text/javascript">
14232  */
14233 /**
14234  * @class Roo.data.HttpProxy
14235  * @extends Roo.data.DataProxy
14236  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14237  * configured to reference a certain URL.<br><br>
14238  * <p>
14239  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14240  * from which the running page was served.<br><br>
14241  * <p>
14242  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14243  * <p>
14244  * Be aware that to enable the browser to parse an XML document, the server must set
14245  * the Content-Type header in the HTTP response to "text/xml".
14246  * @constructor
14247  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14248  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14249  * will be used to make the request.
14250  */
14251 Roo.data.HttpProxy = function(conn){
14252     Roo.data.HttpProxy.superclass.constructor.call(this);
14253     // is conn a conn config or a real conn?
14254     this.conn = conn;
14255     this.useAjax = !conn || !conn.events;
14256   
14257 };
14258
14259 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14260     // thse are take from connection...
14261     
14262     /**
14263      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14264      */
14265     /**
14266      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14267      * extra parameters to each request made by this object. (defaults to undefined)
14268      */
14269     /**
14270      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14271      *  to each request made by this object. (defaults to undefined)
14272      */
14273     /**
14274      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
14275      */
14276     /**
14277      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14278      */
14279      /**
14280      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14281      * @type Boolean
14282      */
14283   
14284
14285     /**
14286      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14287      * @type Boolean
14288      */
14289     /**
14290      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14291      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14292      * a finer-grained basis than the DataProxy events.
14293      */
14294     getConnection : function(){
14295         return this.useAjax ? Roo.Ajax : this.conn;
14296     },
14297
14298     /**
14299      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14300      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14301      * process that block using the passed callback.
14302      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14303      * for the request to the remote server.
14304      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14305      * object into a block of Roo.data.Records.
14306      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14307      * The function must be passed <ul>
14308      * <li>The Record block object</li>
14309      * <li>The "arg" argument from the load function</li>
14310      * <li>A boolean success indicator</li>
14311      * </ul>
14312      * @param {Object} scope The scope in which to call the callback
14313      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14314      */
14315     load : function(params, reader, callback, scope, arg){
14316         if(this.fireEvent("beforeload", this, params) !== false){
14317             var  o = {
14318                 params : params || {},
14319                 request: {
14320                     callback : callback,
14321                     scope : scope,
14322                     arg : arg
14323                 },
14324                 reader: reader,
14325                 callback : this.loadResponse,
14326                 scope: this
14327             };
14328             if(this.useAjax){
14329                 Roo.applyIf(o, this.conn);
14330                 if(this.activeRequest){
14331                     Roo.Ajax.abort(this.activeRequest);
14332                 }
14333                 this.activeRequest = Roo.Ajax.request(o);
14334             }else{
14335                 this.conn.request(o);
14336             }
14337         }else{
14338             callback.call(scope||this, null, arg, false);
14339         }
14340     },
14341
14342     // private
14343     loadResponse : function(o, success, response){
14344         delete this.activeRequest;
14345         if(!success){
14346             this.fireEvent("loadexception", this, o, response);
14347             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14348             return;
14349         }
14350         var result;
14351         try {
14352             result = o.reader.read(response);
14353         }catch(e){
14354             this.fireEvent("loadexception", this, o, response, e);
14355             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14356             return;
14357         }
14358         
14359         this.fireEvent("load", this, o, o.request.arg);
14360         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14361     },
14362
14363     // private
14364     update : function(dataSet){
14365
14366     },
14367
14368     // private
14369     updateResponse : function(dataSet){
14370
14371     }
14372 });/*
14373  * Based on:
14374  * Ext JS Library 1.1.1
14375  * Copyright(c) 2006-2007, Ext JS, LLC.
14376  *
14377  * Originally Released Under LGPL - original licence link has changed is not relivant.
14378  *
14379  * Fork - LGPL
14380  * <script type="text/javascript">
14381  */
14382
14383 /**
14384  * @class Roo.data.ScriptTagProxy
14385  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14386  * other than the originating domain of the running page.<br><br>
14387  * <p>
14388  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
14389  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14390  * <p>
14391  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14392  * source code that is used as the source inside a &lt;script> tag.<br><br>
14393  * <p>
14394  * In order for the browser to process the returned data, the server must wrap the data object
14395  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14396  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14397  * depending on whether the callback name was passed:
14398  * <p>
14399  * <pre><code>
14400 boolean scriptTag = false;
14401 String cb = request.getParameter("callback");
14402 if (cb != null) {
14403     scriptTag = true;
14404     response.setContentType("text/javascript");
14405 } else {
14406     response.setContentType("application/x-json");
14407 }
14408 Writer out = response.getWriter();
14409 if (scriptTag) {
14410     out.write(cb + "(");
14411 }
14412 out.print(dataBlock.toJsonString());
14413 if (scriptTag) {
14414     out.write(");");
14415 }
14416 </pre></code>
14417  *
14418  * @constructor
14419  * @param {Object} config A configuration object.
14420  */
14421 Roo.data.ScriptTagProxy = function(config){
14422     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14423     Roo.apply(this, config);
14424     this.head = document.getElementsByTagName("head")[0];
14425 };
14426
14427 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14428
14429 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14430     /**
14431      * @cfg {String} url The URL from which to request the data object.
14432      */
14433     /**
14434      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14435      */
14436     timeout : 30000,
14437     /**
14438      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14439      * the server the name of the callback function set up by the load call to process the returned data object.
14440      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14441      * javascript output which calls this named function passing the data object as its only parameter.
14442      */
14443     callbackParam : "callback",
14444     /**
14445      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14446      * name to the request.
14447      */
14448     nocache : true,
14449
14450     /**
14451      * Load data from the configured URL, read the data object into
14452      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14453      * process that block using the passed callback.
14454      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14455      * for the request to the remote server.
14456      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14457      * object into a block of Roo.data.Records.
14458      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14459      * The function must be passed <ul>
14460      * <li>The Record block object</li>
14461      * <li>The "arg" argument from the load function</li>
14462      * <li>A boolean success indicator</li>
14463      * </ul>
14464      * @param {Object} scope The scope in which to call the callback
14465      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14466      */
14467     load : function(params, reader, callback, scope, arg){
14468         if(this.fireEvent("beforeload", this, params) !== false){
14469
14470             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14471
14472             var url = this.url;
14473             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14474             if(this.nocache){
14475                 url += "&_dc=" + (new Date().getTime());
14476             }
14477             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14478             var trans = {
14479                 id : transId,
14480                 cb : "stcCallback"+transId,
14481                 scriptId : "stcScript"+transId,
14482                 params : params,
14483                 arg : arg,
14484                 url : url,
14485                 callback : callback,
14486                 scope : scope,
14487                 reader : reader
14488             };
14489             var conn = this;
14490
14491             window[trans.cb] = function(o){
14492                 conn.handleResponse(o, trans);
14493             };
14494
14495             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14496
14497             if(this.autoAbort !== false){
14498                 this.abort();
14499             }
14500
14501             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14502
14503             var script = document.createElement("script");
14504             script.setAttribute("src", url);
14505             script.setAttribute("type", "text/javascript");
14506             script.setAttribute("id", trans.scriptId);
14507             this.head.appendChild(script);
14508
14509             this.trans = trans;
14510         }else{
14511             callback.call(scope||this, null, arg, false);
14512         }
14513     },
14514
14515     // private
14516     isLoading : function(){
14517         return this.trans ? true : false;
14518     },
14519
14520     /**
14521      * Abort the current server request.
14522      */
14523     abort : function(){
14524         if(this.isLoading()){
14525             this.destroyTrans(this.trans);
14526         }
14527     },
14528
14529     // private
14530     destroyTrans : function(trans, isLoaded){
14531         this.head.removeChild(document.getElementById(trans.scriptId));
14532         clearTimeout(trans.timeoutId);
14533         if(isLoaded){
14534             window[trans.cb] = undefined;
14535             try{
14536                 delete window[trans.cb];
14537             }catch(e){}
14538         }else{
14539             // if hasn't been loaded, wait for load to remove it to prevent script error
14540             window[trans.cb] = function(){
14541                 window[trans.cb] = undefined;
14542                 try{
14543                     delete window[trans.cb];
14544                 }catch(e){}
14545             };
14546         }
14547     },
14548
14549     // private
14550     handleResponse : function(o, trans){
14551         this.trans = false;
14552         this.destroyTrans(trans, true);
14553         var result;
14554         try {
14555             result = trans.reader.readRecords(o);
14556         }catch(e){
14557             this.fireEvent("loadexception", this, o, trans.arg, e);
14558             trans.callback.call(trans.scope||window, null, trans.arg, false);
14559             return;
14560         }
14561         this.fireEvent("load", this, o, trans.arg);
14562         trans.callback.call(trans.scope||window, result, trans.arg, true);
14563     },
14564
14565     // private
14566     handleFailure : function(trans){
14567         this.trans = false;
14568         this.destroyTrans(trans, false);
14569         this.fireEvent("loadexception", this, null, trans.arg);
14570         trans.callback.call(trans.scope||window, null, trans.arg, false);
14571     }
14572 });/*
14573  * Based on:
14574  * Ext JS Library 1.1.1
14575  * Copyright(c) 2006-2007, Ext JS, LLC.
14576  *
14577  * Originally Released Under LGPL - original licence link has changed is not relivant.
14578  *
14579  * Fork - LGPL
14580  * <script type="text/javascript">
14581  */
14582
14583 /**
14584  * @class Roo.data.JsonReader
14585  * @extends Roo.data.DataReader
14586  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14587  * based on mappings in a provided Roo.data.Record constructor.
14588  * 
14589  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14590  * in the reply previously. 
14591  * 
14592  * <p>
14593  * Example code:
14594  * <pre><code>
14595 var RecordDef = Roo.data.Record.create([
14596     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14597     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14598 ]);
14599 var myReader = new Roo.data.JsonReader({
14600     totalProperty: "results",    // The property which contains the total dataset size (optional)
14601     root: "rows",                // The property which contains an Array of row objects
14602     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14603 }, RecordDef);
14604 </code></pre>
14605  * <p>
14606  * This would consume a JSON file like this:
14607  * <pre><code>
14608 { 'results': 2, 'rows': [
14609     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14610     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14611 }
14612 </code></pre>
14613  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14614  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14615  * paged from the remote server.
14616  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14617  * @cfg {String} root name of the property which contains the Array of row objects.
14618  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14619  * @cfg {Array} fields Array of field definition objects
14620  * @constructor
14621  * Create a new JsonReader
14622  * @param {Object} meta Metadata configuration options
14623  * @param {Object} recordType Either an Array of field definition objects,
14624  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14625  */
14626 Roo.data.JsonReader = function(meta, recordType){
14627     
14628     meta = meta || {};
14629     // set some defaults:
14630     Roo.applyIf(meta, {
14631         totalProperty: 'total',
14632         successProperty : 'success',
14633         root : 'data',
14634         id : 'id'
14635     });
14636     
14637     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14638 };
14639 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14640     
14641     readerType : 'Json',
14642     
14643     /**
14644      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14645      * Used by Store query builder to append _requestMeta to params.
14646      * 
14647      */
14648     metaFromRemote : false,
14649     /**
14650      * This method is only used by a DataProxy which has retrieved data from a remote server.
14651      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14652      * @return {Object} data A data block which is used by an Roo.data.Store object as
14653      * a cache of Roo.data.Records.
14654      */
14655     read : function(response){
14656         var json = response.responseText;
14657        
14658         var o = /* eval:var:o */ eval("("+json+")");
14659         if(!o) {
14660             throw {message: "JsonReader.read: Json object not found"};
14661         }
14662         
14663         if(o.metaData){
14664             
14665             delete this.ef;
14666             this.metaFromRemote = true;
14667             this.meta = o.metaData;
14668             this.recordType = Roo.data.Record.create(o.metaData.fields);
14669             this.onMetaChange(this.meta, this.recordType, o);
14670         }
14671         return this.readRecords(o);
14672     },
14673
14674     // private function a store will implement
14675     onMetaChange : function(meta, recordType, o){
14676
14677     },
14678
14679     /**
14680          * @ignore
14681          */
14682     simpleAccess: function(obj, subsc) {
14683         return obj[subsc];
14684     },
14685
14686         /**
14687          * @ignore
14688          */
14689     getJsonAccessor: function(){
14690         var re = /[\[\.]/;
14691         return function(expr) {
14692             try {
14693                 return(re.test(expr))
14694                     ? new Function("obj", "return obj." + expr)
14695                     : function(obj){
14696                         return obj[expr];
14697                     };
14698             } catch(e){}
14699             return Roo.emptyFn;
14700         };
14701     }(),
14702
14703     /**
14704      * Create a data block containing Roo.data.Records from an XML document.
14705      * @param {Object} o An object which contains an Array of row objects in the property specified
14706      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14707      * which contains the total size of the dataset.
14708      * @return {Object} data A data block which is used by an Roo.data.Store object as
14709      * a cache of Roo.data.Records.
14710      */
14711     readRecords : function(o){
14712         /**
14713          * After any data loads, the raw JSON data is available for further custom processing.
14714          * @type Object
14715          */
14716         this.o = o;
14717         var s = this.meta, Record = this.recordType,
14718             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14719
14720 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14721         if (!this.ef) {
14722             if(s.totalProperty) {
14723                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14724                 }
14725                 if(s.successProperty) {
14726                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14727                 }
14728                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14729                 if (s.id) {
14730                         var g = this.getJsonAccessor(s.id);
14731                         this.getId = function(rec) {
14732                                 var r = g(rec);  
14733                                 return (r === undefined || r === "") ? null : r;
14734                         };
14735                 } else {
14736                         this.getId = function(){return null;};
14737                 }
14738             this.ef = [];
14739             for(var jj = 0; jj < fl; jj++){
14740                 f = fi[jj];
14741                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14742                 this.ef[jj] = this.getJsonAccessor(map);
14743             }
14744         }
14745
14746         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14747         if(s.totalProperty){
14748             var vt = parseInt(this.getTotal(o), 10);
14749             if(!isNaN(vt)){
14750                 totalRecords = vt;
14751             }
14752         }
14753         if(s.successProperty){
14754             var vs = this.getSuccess(o);
14755             if(vs === false || vs === 'false'){
14756                 success = false;
14757             }
14758         }
14759         var records = [];
14760         for(var i = 0; i < c; i++){
14761                 var n = root[i];
14762             var values = {};
14763             var id = this.getId(n);
14764             for(var j = 0; j < fl; j++){
14765                 f = fi[j];
14766             var v = this.ef[j](n);
14767             if (!f.convert) {
14768                 Roo.log('missing convert for ' + f.name);
14769                 Roo.log(f);
14770                 continue;
14771             }
14772             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14773             }
14774             var record = new Record(values, id);
14775             record.json = n;
14776             records[i] = record;
14777         }
14778         return {
14779             raw : o,
14780             success : success,
14781             records : records,
14782             totalRecords : totalRecords
14783         };
14784     },
14785     // used when loading children.. @see loadDataFromChildren
14786     toLoadData: function(rec)
14787     {
14788         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14789         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14790         return { data : data, total : data.length };
14791         
14792     }
14793 });/*
14794  * Based on:
14795  * Ext JS Library 1.1.1
14796  * Copyright(c) 2006-2007, Ext JS, LLC.
14797  *
14798  * Originally Released Under LGPL - original licence link has changed is not relivant.
14799  *
14800  * Fork - LGPL
14801  * <script type="text/javascript">
14802  */
14803
14804 /**
14805  * @class Roo.data.ArrayReader
14806  * @extends Roo.data.DataReader
14807  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14808  * Each element of that Array represents a row of data fields. The
14809  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14810  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14811  * <p>
14812  * Example code:.
14813  * <pre><code>
14814 var RecordDef = Roo.data.Record.create([
14815     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14816     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14817 ]);
14818 var myReader = new Roo.data.ArrayReader({
14819     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14820 }, RecordDef);
14821 </code></pre>
14822  * <p>
14823  * This would consume an Array like this:
14824  * <pre><code>
14825 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14826   </code></pre>
14827  
14828  * @constructor
14829  * Create a new JsonReader
14830  * @param {Object} meta Metadata configuration options.
14831  * @param {Object|Array} recordType Either an Array of field definition objects
14832  * 
14833  * @cfg {Array} fields Array of field definition objects
14834  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14835  * as specified to {@link Roo.data.Record#create},
14836  * or an {@link Roo.data.Record} object
14837  *
14838  * 
14839  * created using {@link Roo.data.Record#create}.
14840  */
14841 Roo.data.ArrayReader = function(meta, recordType)
14842 {    
14843     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14844 };
14845
14846 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14847     
14848       /**
14849      * Create a data block containing Roo.data.Records from an XML document.
14850      * @param {Object} o An Array of row objects which represents the dataset.
14851      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14852      * a cache of Roo.data.Records.
14853      */
14854     readRecords : function(o)
14855     {
14856         var sid = this.meta ? this.meta.id : null;
14857         var recordType = this.recordType, fields = recordType.prototype.fields;
14858         var records = [];
14859         var root = o;
14860         for(var i = 0; i < root.length; i++){
14861                 var n = root[i];
14862             var values = {};
14863             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14864             for(var j = 0, jlen = fields.length; j < jlen; j++){
14865                 var f = fields.items[j];
14866                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14867                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14868                 v = f.convert(v);
14869                 values[f.name] = v;
14870             }
14871             var record = new recordType(values, id);
14872             record.json = n;
14873             records[records.length] = record;
14874         }
14875         return {
14876             records : records,
14877             totalRecords : records.length
14878         };
14879     },
14880     // used when loading children.. @see loadDataFromChildren
14881     toLoadData: function(rec)
14882     {
14883         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14884         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14885         
14886     }
14887     
14888     
14889 });/*
14890  * - LGPL
14891  * * 
14892  */
14893
14894 /**
14895  * @class Roo.bootstrap.ComboBox
14896  * @extends Roo.bootstrap.TriggerField
14897  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14898  * @cfg {Boolean} append (true|false) default false
14899  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14900  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14901  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14902  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14903  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14904  * @cfg {Boolean} animate default true
14905  * @cfg {Boolean} emptyResultText only for touch device
14906  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14907  * @cfg {String} emptyTitle default ''
14908  * @cfg {Number} width fixed with? experimental
14909  * @constructor
14910  * Create a new ComboBox.
14911  * @param {Object} config Configuration options
14912  */
14913 Roo.bootstrap.ComboBox = function(config){
14914     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14915     this.addEvents({
14916         /**
14917          * @event expand
14918          * Fires when the dropdown list is expanded
14919         * @param {Roo.bootstrap.ComboBox} combo This combo box
14920         */
14921         'expand' : true,
14922         /**
14923          * @event collapse
14924          * Fires when the dropdown list is collapsed
14925         * @param {Roo.bootstrap.ComboBox} combo This combo box
14926         */
14927         'collapse' : true,
14928         /**
14929          * @event beforeselect
14930          * Fires before a list item is selected. Return false to cancel the selection.
14931         * @param {Roo.bootstrap.ComboBox} combo This combo box
14932         * @param {Roo.data.Record} record The data record returned from the underlying store
14933         * @param {Number} index The index of the selected item in the dropdown list
14934         */
14935         'beforeselect' : true,
14936         /**
14937          * @event select
14938          * Fires when a list item is selected
14939         * @param {Roo.bootstrap.ComboBox} combo This combo box
14940         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14941         * @param {Number} index The index of the selected item in the dropdown list
14942         */
14943         'select' : true,
14944         /**
14945          * @event beforequery
14946          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14947          * The event object passed has these properties:
14948         * @param {Roo.bootstrap.ComboBox} combo This combo box
14949         * @param {String} query The query
14950         * @param {Boolean} forceAll true to force "all" query
14951         * @param {Boolean} cancel true to cancel the query
14952         * @param {Object} e The query event object
14953         */
14954         'beforequery': true,
14955          /**
14956          * @event add
14957          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14958         * @param {Roo.bootstrap.ComboBox} combo This combo box
14959         */
14960         'add' : true,
14961         /**
14962          * @event edit
14963          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14964         * @param {Roo.bootstrap.ComboBox} combo This combo box
14965         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14966         */
14967         'edit' : true,
14968         /**
14969          * @event remove
14970          * Fires when the remove value from the combobox array
14971         * @param {Roo.bootstrap.ComboBox} combo This combo box
14972         */
14973         'remove' : true,
14974         /**
14975          * @event afterremove
14976          * Fires when the remove value from the combobox array
14977         * @param {Roo.bootstrap.ComboBox} combo This combo box
14978         */
14979         'afterremove' : true,
14980         /**
14981          * @event specialfilter
14982          * Fires when specialfilter
14983             * @param {Roo.bootstrap.ComboBox} combo This combo box
14984             */
14985         'specialfilter' : true,
14986         /**
14987          * @event tick
14988          * Fires when tick the element
14989             * @param {Roo.bootstrap.ComboBox} combo This combo box
14990             */
14991         'tick' : true,
14992         /**
14993          * @event touchviewdisplay
14994          * Fires when touch view require special display (default is using displayField)
14995             * @param {Roo.bootstrap.ComboBox} combo This combo box
14996             * @param {Object} cfg set html .
14997             */
14998         'touchviewdisplay' : true
14999         
15000     });
15001     
15002     this.item = [];
15003     this.tickItems = [];
15004     
15005     this.selectedIndex = -1;
15006     if(this.mode == 'local'){
15007         if(config.queryDelay === undefined){
15008             this.queryDelay = 10;
15009         }
15010         if(config.minChars === undefined){
15011             this.minChars = 0;
15012         }
15013     }
15014 };
15015
15016 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15017      
15018     /**
15019      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15020      * rendering into an Roo.Editor, defaults to false)
15021      */
15022     /**
15023      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15024      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15025      */
15026     /**
15027      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15028      */
15029     /**
15030      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15031      * the dropdown list (defaults to undefined, with no header element)
15032      */
15033
15034      /**
15035      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15036      */
15037      
15038      /**
15039      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15040      */
15041     listWidth: undefined,
15042     /**
15043      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15044      * mode = 'remote' or 'text' if mode = 'local')
15045      */
15046     displayField: undefined,
15047     
15048     /**
15049      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15050      * mode = 'remote' or 'value' if mode = 'local'). 
15051      * Note: use of a valueField requires the user make a selection
15052      * in order for a value to be mapped.
15053      */
15054     valueField: undefined,
15055     /**
15056      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15057      */
15058     modalTitle : '',
15059     
15060     /**
15061      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15062      * field's data value (defaults to the underlying DOM element's name)
15063      */
15064     hiddenName: undefined,
15065     /**
15066      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15067      */
15068     listClass: '',
15069     /**
15070      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15071      */
15072     selectedClass: 'active',
15073     
15074     /**
15075      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15076      */
15077     shadow:'sides',
15078     /**
15079      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15080      * anchor positions (defaults to 'tl-bl')
15081      */
15082     listAlign: 'tl-bl?',
15083     /**
15084      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15085      */
15086     maxHeight: 300,
15087     /**
15088      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15089      * query specified by the allQuery config option (defaults to 'query')
15090      */
15091     triggerAction: 'query',
15092     /**
15093      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15094      * (defaults to 4, does not apply if editable = false)
15095      */
15096     minChars : 4,
15097     /**
15098      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15099      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15100      */
15101     typeAhead: false,
15102     /**
15103      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15104      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15105      */
15106     queryDelay: 500,
15107     /**
15108      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15109      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15110      */
15111     pageSize: 0,
15112     /**
15113      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15114      * when editable = true (defaults to false)
15115      */
15116     selectOnFocus:false,
15117     /**
15118      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15119      */
15120     queryParam: 'query',
15121     /**
15122      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15123      * when mode = 'remote' (defaults to 'Loading...')
15124      */
15125     loadingText: 'Loading...',
15126     /**
15127      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15128      */
15129     resizable: false,
15130     /**
15131      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15132      */
15133     handleHeight : 8,
15134     /**
15135      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15136      * traditional select (defaults to true)
15137      */
15138     editable: true,
15139     /**
15140      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15141      */
15142     allQuery: '',
15143     /**
15144      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15145      */
15146     mode: 'remote',
15147     /**
15148      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15149      * listWidth has a higher value)
15150      */
15151     minListWidth : 70,
15152     /**
15153      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15154      * allow the user to set arbitrary text into the field (defaults to false)
15155      */
15156     forceSelection:false,
15157     /**
15158      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15159      * if typeAhead = true (defaults to 250)
15160      */
15161     typeAheadDelay : 250,
15162     /**
15163      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15164      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15165      */
15166     valueNotFoundText : undefined,
15167     /**
15168      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15169      */
15170     blockFocus : false,
15171     
15172     /**
15173      * @cfg {Boolean} disableClear Disable showing of clear button.
15174      */
15175     disableClear : false,
15176     /**
15177      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15178      */
15179     alwaysQuery : false,
15180     
15181     /**
15182      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15183      */
15184     multiple : false,
15185     
15186     /**
15187      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     invalidClass : "has-warning",
15190     
15191     /**
15192      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15193      */
15194     validClass : "has-success",
15195     
15196     /**
15197      * @cfg {Boolean} specialFilter (true|false) special filter default false
15198      */
15199     specialFilter : false,
15200     
15201     /**
15202      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15203      */
15204     mobileTouchView : true,
15205     
15206     /**
15207      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15208      */
15209     useNativeIOS : false,
15210     
15211     /**
15212      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15213      */
15214     mobile_restrict_height : false,
15215     
15216     ios_options : false,
15217     
15218     //private
15219     addicon : false,
15220     editicon: false,
15221     
15222     page: 0,
15223     hasQuery: false,
15224     append: false,
15225     loadNext: false,
15226     autoFocus : true,
15227     tickable : false,
15228     btnPosition : 'right',
15229     triggerList : true,
15230     showToggleBtn : true,
15231     animate : true,
15232     emptyResultText: 'Empty',
15233     triggerText : 'Select',
15234     emptyTitle : '',
15235     width : false,
15236     
15237     // element that contains real text value.. (when hidden is used..)
15238     
15239     getAutoCreate : function()
15240     {   
15241         var cfg = false;
15242         //render
15243         /*
15244          * Render classic select for iso
15245          */
15246         
15247         if(Roo.isIOS && this.useNativeIOS){
15248             cfg = this.getAutoCreateNativeIOS();
15249             return cfg;
15250         }
15251         
15252         /*
15253          * Touch Devices
15254          */
15255         
15256         if(Roo.isTouch && this.mobileTouchView){
15257             cfg = this.getAutoCreateTouchView();
15258             return cfg;;
15259         }
15260         
15261         /*
15262          *  Normal ComboBox
15263          */
15264         if(!this.tickable){
15265             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15266             return cfg;
15267         }
15268         
15269         /*
15270          *  ComboBox with tickable selections
15271          */
15272              
15273         var align = this.labelAlign || this.parentLabelAlign();
15274         
15275         cfg = {
15276             cls : 'form-group roo-combobox-tickable' //input-group
15277         };
15278         
15279         var btn_text_select = '';
15280         var btn_text_done = '';
15281         var btn_text_cancel = '';
15282         
15283         if (this.btn_text_show) {
15284             btn_text_select = 'Select';
15285             btn_text_done = 'Done';
15286             btn_text_cancel = 'Cancel'; 
15287         }
15288         
15289         var buttons = {
15290             tag : 'div',
15291             cls : 'tickable-buttons',
15292             cn : [
15293                 {
15294                     tag : 'button',
15295                     type : 'button',
15296                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15297                     //html : this.triggerText
15298                     html: btn_text_select
15299                 },
15300                 {
15301                     tag : 'button',
15302                     type : 'button',
15303                     name : 'ok',
15304                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15305                     //html : 'Done'
15306                     html: btn_text_done
15307                 },
15308                 {
15309                     tag : 'button',
15310                     type : 'button',
15311                     name : 'cancel',
15312                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15313                     //html : 'Cancel'
15314                     html: btn_text_cancel
15315                 }
15316             ]
15317         };
15318         
15319         if(this.editable){
15320             buttons.cn.unshift({
15321                 tag: 'input',
15322                 cls: 'roo-select2-search-field-input'
15323             });
15324         }
15325         
15326         var _this = this;
15327         
15328         Roo.each(buttons.cn, function(c){
15329             if (_this.size) {
15330                 c.cls += ' btn-' + _this.size;
15331             }
15332
15333             if (_this.disabled) {
15334                 c.disabled = true;
15335             }
15336         });
15337         
15338         var box = {
15339             tag: 'div',
15340             style : 'display: contents',
15341             cn: [
15342                 {
15343                     tag: 'input',
15344                     type : 'hidden',
15345                     cls: 'form-hidden-field'
15346                 },
15347                 {
15348                     tag: 'ul',
15349                     cls: 'roo-select2-choices',
15350                     cn:[
15351                         {
15352                             tag: 'li',
15353                             cls: 'roo-select2-search-field',
15354                             cn: [
15355                                 buttons
15356                             ]
15357                         }
15358                     ]
15359                 }
15360             ]
15361         };
15362         
15363         var combobox = {
15364             cls: 'roo-select2-container input-group roo-select2-container-multi',
15365             cn: [
15366                 
15367                 box
15368 //                {
15369 //                    tag: 'ul',
15370 //                    cls: 'typeahead typeahead-long dropdown-menu',
15371 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15372 //                }
15373             ]
15374         };
15375         
15376         if(this.hasFeedback && !this.allowBlank){
15377             
15378             var feedback = {
15379                 tag: 'span',
15380                 cls: 'glyphicon form-control-feedback'
15381             };
15382
15383             combobox.cn.push(feedback);
15384         }
15385         
15386         
15387         
15388         var indicator = {
15389             tag : 'i',
15390             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15391             tooltip : 'This field is required'
15392         };
15393         if (Roo.bootstrap.version == 4) {
15394             indicator = {
15395                 tag : 'i',
15396                 style : 'display:none'
15397             };
15398         }
15399         if (align ==='left' && this.fieldLabel.length) {
15400             
15401             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15402             
15403             cfg.cn = [
15404                 indicator,
15405                 {
15406                     tag: 'label',
15407                     'for' :  id,
15408                     cls : 'control-label col-form-label',
15409                     html : this.fieldLabel
15410
15411                 },
15412                 {
15413                     cls : "", 
15414                     cn: [
15415                         combobox
15416                     ]
15417                 }
15418
15419             ];
15420             
15421             var labelCfg = cfg.cn[1];
15422             var contentCfg = cfg.cn[2];
15423             
15424
15425             if(this.indicatorpos == 'right'){
15426                 
15427                 cfg.cn = [
15428                     {
15429                         tag: 'label',
15430                         'for' :  id,
15431                         cls : 'control-label col-form-label',
15432                         cn : [
15433                             {
15434                                 tag : 'span',
15435                                 html : this.fieldLabel
15436                             },
15437                             indicator
15438                         ]
15439                     },
15440                     {
15441                         cls : "",
15442                         cn: [
15443                             combobox
15444                         ]
15445                     }
15446
15447                 ];
15448                 
15449                 
15450                 
15451                 labelCfg = cfg.cn[0];
15452                 contentCfg = cfg.cn[1];
15453             
15454             }
15455             
15456             if(this.labelWidth > 12){
15457                 labelCfg.style = "width: " + this.labelWidth + 'px';
15458             }
15459             if(this.width * 1 > 0){
15460                 contentCfg.style = "width: " + this.width + 'px';
15461             }
15462             if(this.labelWidth < 13 && this.labelmd == 0){
15463                 this.labelmd = this.labelWidth;
15464             }
15465             
15466             if(this.labellg > 0){
15467                 labelCfg.cls += ' col-lg-' + this.labellg;
15468                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15469             }
15470             
15471             if(this.labelmd > 0){
15472                 labelCfg.cls += ' col-md-' + this.labelmd;
15473                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15474             }
15475             
15476             if(this.labelsm > 0){
15477                 labelCfg.cls += ' col-sm-' + this.labelsm;
15478                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15479             }
15480             
15481             if(this.labelxs > 0){
15482                 labelCfg.cls += ' col-xs-' + this.labelxs;
15483                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15484             }
15485                 
15486                 
15487         } else if ( this.fieldLabel.length) {
15488 //                Roo.log(" label");
15489                  cfg.cn = [
15490                    indicator,
15491                     {
15492                         tag: 'label',
15493                         //cls : 'input-group-addon',
15494                         html : this.fieldLabel
15495                     },
15496                     combobox
15497                 ];
15498                 
15499                 if(this.indicatorpos == 'right'){
15500                     cfg.cn = [
15501                         {
15502                             tag: 'label',
15503                             //cls : 'input-group-addon',
15504                             html : this.fieldLabel
15505                         },
15506                         indicator,
15507                         combobox
15508                     ];
15509                     
15510                 }
15511
15512         } else {
15513             
15514 //                Roo.log(" no label && no align");
15515                 cfg = combobox
15516                      
15517                 
15518         }
15519          
15520         var settings=this;
15521         ['xs','sm','md','lg'].map(function(size){
15522             if (settings[size]) {
15523                 cfg.cls += ' col-' + size + '-' + settings[size];
15524             }
15525         });
15526         
15527         return cfg;
15528         
15529     },
15530     
15531     _initEventsCalled : false,
15532     
15533     // private
15534     initEvents: function()
15535     {   
15536         if (this._initEventsCalled) { // as we call render... prevent looping...
15537             return;
15538         }
15539         this._initEventsCalled = true;
15540         
15541         if (!this.store) {
15542             throw "can not find store for combo";
15543         }
15544         
15545         this.indicator = this.indicatorEl();
15546         
15547         this.store = Roo.factory(this.store, Roo.data);
15548         this.store.parent = this;
15549         
15550         // if we are building from html. then this element is so complex, that we can not really
15551         // use the rendered HTML.
15552         // so we have to trash and replace the previous code.
15553         if (Roo.XComponent.build_from_html) {
15554             // remove this element....
15555             var e = this.el.dom, k=0;
15556             while (e ) { e = e.previousSibling;  ++k;}
15557
15558             this.el.remove();
15559             
15560             this.el=false;
15561             this.rendered = false;
15562             
15563             this.render(this.parent().getChildContainer(true), k);
15564         }
15565         
15566         if(Roo.isIOS && this.useNativeIOS){
15567             this.initIOSView();
15568             return;
15569         }
15570         
15571         /*
15572          * Touch Devices
15573          */
15574         
15575         if(Roo.isTouch && this.mobileTouchView){
15576             this.initTouchView();
15577             return;
15578         }
15579         
15580         if(this.tickable){
15581             this.initTickableEvents();
15582             return;
15583         }
15584         
15585         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15586         
15587         if(this.hiddenName){
15588             
15589             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15590             
15591             this.hiddenField.dom.value =
15592                 this.hiddenValue !== undefined ? this.hiddenValue :
15593                 this.value !== undefined ? this.value : '';
15594
15595             // prevent input submission
15596             this.el.dom.removeAttribute('name');
15597             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15598              
15599              
15600         }
15601         //if(Roo.isGecko){
15602         //    this.el.dom.setAttribute('autocomplete', 'off');
15603         //}
15604         
15605         var cls = 'x-combo-list';
15606         
15607         //this.list = new Roo.Layer({
15608         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15609         //});
15610         
15611         var _this = this;
15612         
15613         (function(){
15614             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15615             _this.list.setWidth(lw);
15616         }).defer(100);
15617         
15618         this.list.on('mouseover', this.onViewOver, this);
15619         this.list.on('mousemove', this.onViewMove, this);
15620         this.list.on('scroll', this.onViewScroll, this);
15621         
15622         /*
15623         this.list.swallowEvent('mousewheel');
15624         this.assetHeight = 0;
15625
15626         if(this.title){
15627             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15628             this.assetHeight += this.header.getHeight();
15629         }
15630
15631         this.innerList = this.list.createChild({cls:cls+'-inner'});
15632         this.innerList.on('mouseover', this.onViewOver, this);
15633         this.innerList.on('mousemove', this.onViewMove, this);
15634         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15635         
15636         if(this.allowBlank && !this.pageSize && !this.disableClear){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.Toolbar(this.footer);
15639            
15640         }
15641         if(this.pageSize){
15642             this.footer = this.list.createChild({cls:cls+'-ft'});
15643             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15644                     {pageSize: this.pageSize});
15645             
15646         }
15647         
15648         if (this.pageTb && this.allowBlank && !this.disableClear) {
15649             var _this = this;
15650             this.pageTb.add(new Roo.Toolbar.Fill(), {
15651                 cls: 'x-btn-icon x-btn-clear',
15652                 text: '&#160;',
15653                 handler: function()
15654                 {
15655                     _this.collapse();
15656                     _this.clearValue();
15657                     _this.onSelect(false, -1);
15658                 }
15659             });
15660         }
15661         if (this.footer) {
15662             this.assetHeight += this.footer.getHeight();
15663         }
15664         */
15665             
15666         if(!this.tpl){
15667             this.tpl = Roo.bootstrap.version == 4 ?
15668                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15669                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15670         }
15671
15672         this.view = new Roo.View(this.list, this.tpl, {
15673             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15674         });
15675         //this.view.wrapEl.setDisplayed(false);
15676         this.view.on('click', this.onViewClick, this);
15677         
15678         
15679         this.store.on('beforeload', this.onBeforeLoad, this);
15680         this.store.on('load', this.onLoad, this);
15681         this.store.on('loadexception', this.onLoadException, this);
15682         /*
15683         if(this.resizable){
15684             this.resizer = new Roo.Resizable(this.list,  {
15685                pinned:true, handles:'se'
15686             });
15687             this.resizer.on('resize', function(r, w, h){
15688                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15689                 this.listWidth = w;
15690                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15691                 this.restrictHeight();
15692             }, this);
15693             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15694         }
15695         */
15696         if(!this.editable){
15697             this.editable = true;
15698             this.setEditable(false);
15699         }
15700         
15701         /*
15702         
15703         if (typeof(this.events.add.listeners) != 'undefined') {
15704             
15705             this.addicon = this.wrap.createChild(
15706                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15707        
15708             this.addicon.on('click', function(e) {
15709                 this.fireEvent('add', this);
15710             }, this);
15711         }
15712         if (typeof(this.events.edit.listeners) != 'undefined') {
15713             
15714             this.editicon = this.wrap.createChild(
15715                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15716             if (this.addicon) {
15717                 this.editicon.setStyle('margin-left', '40px');
15718             }
15719             this.editicon.on('click', function(e) {
15720                 
15721                 // we fire even  if inothing is selected..
15722                 this.fireEvent('edit', this, this.lastData );
15723                 
15724             }, this);
15725         }
15726         */
15727         
15728         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15729             "up" : function(e){
15730                 this.inKeyMode = true;
15731                 this.selectPrev();
15732             },
15733
15734             "down" : function(e){
15735                 if(!this.isExpanded()){
15736                     this.onTriggerClick();
15737                 }else{
15738                     this.inKeyMode = true;
15739                     this.selectNext();
15740                 }
15741             },
15742
15743             "enter" : function(e){
15744 //                this.onViewClick();
15745                 //return true;
15746                 this.collapse();
15747                 
15748                 if(this.fireEvent("specialkey", this, e)){
15749                     this.onViewClick(false);
15750                 }
15751                 
15752                 return true;
15753             },
15754
15755             "esc" : function(e){
15756                 this.collapse();
15757             },
15758
15759             "tab" : function(e){
15760                 this.collapse();
15761                 
15762                 if(this.fireEvent("specialkey", this, e)){
15763                     this.onViewClick(false);
15764                 }
15765                 
15766                 return true;
15767             },
15768
15769             scope : this,
15770
15771             doRelay : function(foo, bar, hname){
15772                 if(hname == 'down' || this.scope.isExpanded()){
15773                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15774                 }
15775                 return true;
15776             },
15777
15778             forceKeyDown: true
15779         });
15780         
15781         
15782         this.queryDelay = Math.max(this.queryDelay || 10,
15783                 this.mode == 'local' ? 10 : 250);
15784         
15785         
15786         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15787         
15788         if(this.typeAhead){
15789             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15790         }
15791         if(this.editable !== false){
15792             this.inputEl().on("keyup", this.onKeyUp, this);
15793         }
15794         if(this.forceSelection){
15795             this.inputEl().on('blur', this.doForce, this);
15796         }
15797         
15798         if(this.multiple){
15799             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15800             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15801         }
15802     },
15803     
15804     initTickableEvents: function()
15805     {   
15806         this.createList();
15807         
15808         if(this.hiddenName){
15809             
15810             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15811             
15812             this.hiddenField.dom.value =
15813                 this.hiddenValue !== undefined ? this.hiddenValue :
15814                 this.value !== undefined ? this.value : '';
15815
15816             // prevent input submission
15817             this.el.dom.removeAttribute('name');
15818             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15819              
15820              
15821         }
15822         
15823 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15824         
15825         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15826         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15827         if(this.triggerList){
15828             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15829         }
15830          
15831         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15832         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15833         
15834         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15835         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15836         
15837         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15838         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15839         
15840         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15841         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15842         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15843         
15844         this.okBtn.hide();
15845         this.cancelBtn.hide();
15846         
15847         var _this = this;
15848         
15849         (function(){
15850             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15851             _this.list.setWidth(lw);
15852         }).defer(100);
15853         
15854         this.list.on('mouseover', this.onViewOver, this);
15855         this.list.on('mousemove', this.onViewMove, this);
15856         
15857         this.list.on('scroll', this.onViewScroll, this);
15858         
15859         if(!this.tpl){
15860             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15861                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15862         }
15863
15864         this.view = new Roo.View(this.list, this.tpl, {
15865             singleSelect:true,
15866             tickable:true,
15867             parent:this,
15868             store: this.store,
15869             selectedClass: this.selectedClass
15870         });
15871         
15872         //this.view.wrapEl.setDisplayed(false);
15873         this.view.on('click', this.onViewClick, this);
15874         
15875         
15876         
15877         this.store.on('beforeload', this.onBeforeLoad, this);
15878         this.store.on('load', this.onLoad, this);
15879         this.store.on('loadexception', this.onLoadException, this);
15880         
15881         if(this.editable){
15882             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15883                 "up" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectPrev();
15886                 },
15887
15888                 "down" : function(e){
15889                     this.inKeyMode = true;
15890                     this.selectNext();
15891                 },
15892
15893                 "enter" : function(e){
15894                     if(this.fireEvent("specialkey", this, e)){
15895                         this.onViewClick(false);
15896                     }
15897                     
15898                     return true;
15899                 },
15900
15901                 "esc" : function(e){
15902                     this.onTickableFooterButtonClick(e, false, false);
15903                 },
15904
15905                 "tab" : function(e){
15906                     this.fireEvent("specialkey", this, e);
15907                     
15908                     this.onTickableFooterButtonClick(e, false, false);
15909                     
15910                     return true;
15911                 },
15912
15913                 scope : this,
15914
15915                 doRelay : function(e, fn, key){
15916                     if(this.scope.isExpanded()){
15917                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15918                     }
15919                     return true;
15920                 },
15921
15922                 forceKeyDown: true
15923             });
15924         }
15925         
15926         this.queryDelay = Math.max(this.queryDelay || 10,
15927                 this.mode == 'local' ? 10 : 250);
15928         
15929         
15930         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15931         
15932         if(this.typeAhead){
15933             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15934         }
15935         
15936         if(this.editable !== false){
15937             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15938         }
15939         
15940         this.indicator = this.indicatorEl();
15941         
15942         if(this.indicator){
15943             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15944             this.indicator.hide();
15945         }
15946         
15947     },
15948
15949     onDestroy : function(){
15950         if(this.view){
15951             this.view.setStore(null);
15952             this.view.el.removeAllListeners();
15953             this.view.el.remove();
15954             this.view.purgeListeners();
15955         }
15956         if(this.list){
15957             this.list.dom.innerHTML  = '';
15958         }
15959         
15960         if(this.store){
15961             this.store.un('beforeload', this.onBeforeLoad, this);
15962             this.store.un('load', this.onLoad, this);
15963             this.store.un('loadexception', this.onLoadException, this);
15964         }
15965         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15966     },
15967
15968     // private
15969     fireKey : function(e){
15970         if(e.isNavKeyPress() && !this.list.isVisible()){
15971             this.fireEvent("specialkey", this, e);
15972         }
15973     },
15974
15975     // private
15976     onResize: function(w, h)
15977     {
15978         
15979         
15980 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15981 //        
15982 //        if(typeof w != 'number'){
15983 //            // we do not handle it!?!?
15984 //            return;
15985 //        }
15986 //        var tw = this.trigger.getWidth();
15987 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15988 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15989 //        var x = w - tw;
15990 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15991 //            
15992 //        //this.trigger.setStyle('left', x+'px');
15993 //        
15994 //        if(this.list && this.listWidth === undefined){
15995 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15996 //            this.list.setWidth(lw);
15997 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15998 //        }
15999         
16000     
16001         
16002     },
16003
16004     /**
16005      * Allow or prevent the user from directly editing the field text.  If false is passed,
16006      * the user will only be able to select from the items defined in the dropdown list.  This method
16007      * is the runtime equivalent of setting the 'editable' config option at config time.
16008      * @param {Boolean} value True to allow the user to directly edit the field text
16009      */
16010     setEditable : function(value){
16011         if(value == this.editable){
16012             return;
16013         }
16014         this.editable = value;
16015         if(!value){
16016             this.inputEl().dom.setAttribute('readOnly', true);
16017             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16018             this.inputEl().addClass('x-combo-noedit');
16019         }else{
16020             this.inputEl().dom.setAttribute('readOnly', false);
16021             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16022             this.inputEl().removeClass('x-combo-noedit');
16023         }
16024     },
16025
16026     // private
16027     
16028     onBeforeLoad : function(combo,opts){
16029         if(!this.hasFocus){
16030             return;
16031         }
16032          if (!opts.add) {
16033             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16034          }
16035         this.restrictHeight();
16036         this.selectedIndex = -1;
16037     },
16038
16039     // private
16040     onLoad : function(){
16041         
16042         this.hasQuery = false;
16043         
16044         if(!this.hasFocus){
16045             return;
16046         }
16047         
16048         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16049             this.loading.hide();
16050         }
16051         
16052         if(this.store.getCount() > 0){
16053             
16054             this.expand();
16055             this.restrictHeight();
16056             if(this.lastQuery == this.allQuery){
16057                 if(this.editable && !this.tickable){
16058                     this.inputEl().dom.select();
16059                 }
16060                 
16061                 if(
16062                     !this.selectByValue(this.value, true) &&
16063                     this.autoFocus && 
16064                     (
16065                         !this.store.lastOptions ||
16066                         typeof(this.store.lastOptions.add) == 'undefined' || 
16067                         this.store.lastOptions.add != true
16068                     )
16069                 ){
16070                     this.select(0, true);
16071                 }
16072             }else{
16073                 if(this.autoFocus){
16074                     this.selectNext();
16075                 }
16076                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16077                     this.taTask.delay(this.typeAheadDelay);
16078                 }
16079             }
16080         }else{
16081             this.onEmptyResults();
16082         }
16083         
16084         //this.el.focus();
16085     },
16086     // private
16087     onLoadException : function()
16088     {
16089         this.hasQuery = false;
16090         
16091         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16092             this.loading.hide();
16093         }
16094         
16095         if(this.tickable && this.editable){
16096             return;
16097         }
16098         
16099         this.collapse();
16100         // only causes errors at present
16101         //Roo.log(this.store.reader.jsonData);
16102         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16103             // fixme
16104             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16105         //}
16106         
16107         
16108     },
16109     // private
16110     onTypeAhead : function(){
16111         if(this.store.getCount() > 0){
16112             var r = this.store.getAt(0);
16113             var newValue = r.data[this.displayField];
16114             var len = newValue.length;
16115             var selStart = this.getRawValue().length;
16116             
16117             if(selStart != len){
16118                 this.setRawValue(newValue);
16119                 this.selectText(selStart, newValue.length);
16120             }
16121         }
16122     },
16123
16124     // private
16125     onSelect : function(record, index){
16126         
16127         if(this.fireEvent('beforeselect', this, record, index) !== false){
16128         
16129             this.setFromData(index > -1 ? record.data : false);
16130             
16131             this.collapse();
16132             this.fireEvent('select', this, record, index);
16133         }
16134     },
16135
16136     /**
16137      * Returns the currently selected field value or empty string if no value is set.
16138      * @return {String} value The selected value
16139      */
16140     getValue : function()
16141     {
16142         if(Roo.isIOS && this.useNativeIOS){
16143             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16144         }
16145         
16146         if(this.multiple){
16147             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16148         }
16149         
16150         if(this.valueField){
16151             return typeof this.value != 'undefined' ? this.value : '';
16152         }else{
16153             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16154         }
16155     },
16156     
16157     getRawValue : function()
16158     {
16159         if(Roo.isIOS && this.useNativeIOS){
16160             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16161         }
16162         
16163         var v = this.inputEl().getValue();
16164         
16165         return v;
16166     },
16167
16168     /**
16169      * Clears any text/value currently set in the field
16170      */
16171     clearValue : function(){
16172         
16173         if(this.hiddenField){
16174             this.hiddenField.dom.value = '';
16175         }
16176         this.value = '';
16177         this.setRawValue('');
16178         this.lastSelectionText = '';
16179         this.lastData = false;
16180         
16181         var close = this.closeTriggerEl();
16182         
16183         if(close){
16184             close.hide();
16185         }
16186         
16187         this.validate();
16188         
16189     },
16190
16191     /**
16192      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16193      * will be displayed in the field.  If the value does not match the data value of an existing item,
16194      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16195      * Otherwise the field will be blank (although the value will still be set).
16196      * @param {String} value The value to match
16197      */
16198     setValue : function(v)
16199     {
16200         if(Roo.isIOS && this.useNativeIOS){
16201             this.setIOSValue(v);
16202             return;
16203         }
16204         
16205         if(this.multiple){
16206             this.syncValue();
16207             return;
16208         }
16209         
16210         var text = v;
16211         if(this.valueField){
16212             var r = this.findRecord(this.valueField, v);
16213             if(r){
16214                 text = r.data[this.displayField];
16215             }else if(this.valueNotFoundText !== undefined){
16216                 text = this.valueNotFoundText;
16217             }
16218         }
16219         this.lastSelectionText = text;
16220         if(this.hiddenField){
16221             this.hiddenField.dom.value = v;
16222         }
16223         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16224         this.value = v;
16225         
16226         var close = this.closeTriggerEl();
16227         
16228         if(close){
16229             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16230         }
16231         
16232         this.validate();
16233     },
16234     /**
16235      * @property {Object} the last set data for the element
16236      */
16237     
16238     lastData : false,
16239     /**
16240      * Sets the value of the field based on a object which is related to the record format for the store.
16241      * @param {Object} value the value to set as. or false on reset?
16242      */
16243     setFromData : function(o){
16244         
16245         if(this.multiple){
16246             this.addItem(o);
16247             return;
16248         }
16249             
16250         var dv = ''; // display value
16251         var vv = ''; // value value..
16252         this.lastData = o;
16253         if (this.displayField) {
16254             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16255         } else {
16256             // this is an error condition!!!
16257             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16258         }
16259         
16260         if(this.valueField){
16261             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16262         }
16263         
16264         var close = this.closeTriggerEl();
16265         
16266         if(close){
16267             if(dv.length || vv * 1 > 0){
16268                 close.show() ;
16269                 this.blockFocus=true;
16270             } else {
16271                 close.hide();
16272             }             
16273         }
16274         
16275         if(this.hiddenField){
16276             this.hiddenField.dom.value = vv;
16277             
16278             this.lastSelectionText = dv;
16279             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16280             this.value = vv;
16281             return;
16282         }
16283         // no hidden field.. - we store the value in 'value', but still display
16284         // display field!!!!
16285         this.lastSelectionText = dv;
16286         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16287         this.value = vv;
16288         
16289         
16290         
16291     },
16292     // private
16293     reset : function(){
16294         // overridden so that last data is reset..
16295         
16296         if(this.multiple){
16297             this.clearItem();
16298             return;
16299         }
16300         
16301         this.setValue(this.originalValue);
16302         //this.clearInvalid();
16303         this.lastData = false;
16304         if (this.view) {
16305             this.view.clearSelections();
16306         }
16307         
16308         this.validate();
16309     },
16310     // private
16311     findRecord : function(prop, value){
16312         var record;
16313         if(this.store.getCount() > 0){
16314             this.store.each(function(r){
16315                 if(r.data[prop] == value){
16316                     record = r;
16317                     return false;
16318                 }
16319                 return true;
16320             });
16321         }
16322         return record;
16323     },
16324     
16325     getName: function()
16326     {
16327         // returns hidden if it's set..
16328         if (!this.rendered) {return ''};
16329         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16330         
16331     },
16332     // private
16333     onViewMove : function(e, t){
16334         this.inKeyMode = false;
16335     },
16336
16337     // private
16338     onViewOver : function(e, t){
16339         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16340             return;
16341         }
16342         var item = this.view.findItemFromChild(t);
16343         
16344         if(item){
16345             var index = this.view.indexOf(item);
16346             this.select(index, false);
16347         }
16348     },
16349
16350     // private
16351     onViewClick : function(view, doFocus, el, e)
16352     {
16353         var index = this.view.getSelectedIndexes()[0];
16354         
16355         var r = this.store.getAt(index);
16356         
16357         if(this.tickable){
16358             
16359             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16360                 return;
16361             }
16362             
16363             var rm = false;
16364             var _this = this;
16365             
16366             Roo.each(this.tickItems, function(v,k){
16367                 
16368                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16369                     Roo.log(v);
16370                     _this.tickItems.splice(k, 1);
16371                     
16372                     if(typeof(e) == 'undefined' && view == false){
16373                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16374                     }
16375                     
16376                     rm = true;
16377                     return;
16378                 }
16379             });
16380             
16381             if(rm){
16382                 return;
16383             }
16384             
16385             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16386                 this.tickItems.push(r.data);
16387             }
16388             
16389             if(typeof(e) == 'undefined' && view == false){
16390                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16391             }
16392                     
16393             return;
16394         }
16395         
16396         if(r){
16397             this.onSelect(r, index);
16398         }
16399         if(doFocus !== false && !this.blockFocus){
16400             this.inputEl().focus();
16401         }
16402     },
16403
16404     // private
16405     restrictHeight : function(){
16406         //this.innerList.dom.style.height = '';
16407         //var inner = this.innerList.dom;
16408         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16409         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16410         //this.list.beginUpdate();
16411         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16412         this.list.alignTo(this.inputEl(), this.listAlign);
16413         this.list.alignTo(this.inputEl(), this.listAlign);
16414         //this.list.endUpdate();
16415     },
16416
16417     // private
16418     onEmptyResults : function(){
16419         
16420         if(this.tickable && this.editable){
16421             this.hasFocus = false;
16422             this.restrictHeight();
16423             return;
16424         }
16425         
16426         this.collapse();
16427     },
16428
16429     /**
16430      * Returns true if the dropdown list is expanded, else false.
16431      */
16432     isExpanded : function(){
16433         return this.list.isVisible();
16434     },
16435
16436     /**
16437      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16438      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16439      * @param {String} value The data value of the item to select
16440      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16441      * selected item if it is not currently in view (defaults to true)
16442      * @return {Boolean} True if the value matched an item in the list, else false
16443      */
16444     selectByValue : function(v, scrollIntoView){
16445         if(v !== undefined && v !== null){
16446             var r = this.findRecord(this.valueField || this.displayField, v);
16447             if(r){
16448                 this.select(this.store.indexOf(r), scrollIntoView);
16449                 return true;
16450             }
16451         }
16452         return false;
16453     },
16454
16455     /**
16456      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16457      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16458      * @param {Number} index The zero-based index of the list item to select
16459      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16460      * selected item if it is not currently in view (defaults to true)
16461      */
16462     select : function(index, scrollIntoView){
16463         this.selectedIndex = index;
16464         this.view.select(index);
16465         if(scrollIntoView !== false){
16466             var el = this.view.getNode(index);
16467             /*
16468              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16469              */
16470             if(el){
16471                 this.list.scrollChildIntoView(el, false);
16472             }
16473         }
16474     },
16475
16476     // private
16477     selectNext : function(){
16478         var ct = this.store.getCount();
16479         if(ct > 0){
16480             if(this.selectedIndex == -1){
16481                 this.select(0);
16482             }else if(this.selectedIndex < ct-1){
16483                 this.select(this.selectedIndex+1);
16484             }
16485         }
16486     },
16487
16488     // private
16489     selectPrev : function(){
16490         var ct = this.store.getCount();
16491         if(ct > 0){
16492             if(this.selectedIndex == -1){
16493                 this.select(0);
16494             }else if(this.selectedIndex != 0){
16495                 this.select(this.selectedIndex-1);
16496             }
16497         }
16498     },
16499
16500     // private
16501     onKeyUp : function(e){
16502         if(this.editable !== false && !e.isSpecialKey()){
16503             this.lastKey = e.getKey();
16504             this.dqTask.delay(this.queryDelay);
16505         }
16506     },
16507
16508     // private
16509     validateBlur : function(){
16510         return !this.list || !this.list.isVisible();   
16511     },
16512
16513     // private
16514     initQuery : function(){
16515         
16516         var v = this.getRawValue();
16517         
16518         if(this.tickable && this.editable){
16519             v = this.tickableInputEl().getValue();
16520         }
16521         
16522         this.doQuery(v);
16523     },
16524
16525     // private
16526     doForce : function(){
16527         if(this.inputEl().dom.value.length > 0){
16528             this.inputEl().dom.value =
16529                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16530              
16531         }
16532     },
16533
16534     /**
16535      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16536      * query allowing the query action to be canceled if needed.
16537      * @param {String} query The SQL query to execute
16538      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16539      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16540      * saved in the current store (defaults to false)
16541      */
16542     doQuery : function(q, forceAll){
16543         
16544         if(q === undefined || q === null){
16545             q = '';
16546         }
16547         var qe = {
16548             query: q,
16549             forceAll: forceAll,
16550             combo: this,
16551             cancel:false
16552         };
16553         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16554             return false;
16555         }
16556         q = qe.query;
16557         
16558         forceAll = qe.forceAll;
16559         if(forceAll === true || (q.length >= this.minChars)){
16560             
16561             this.hasQuery = true;
16562             
16563             if(this.lastQuery != q || this.alwaysQuery){
16564                 this.lastQuery = q;
16565                 if(this.mode == 'local'){
16566                     this.selectedIndex = -1;
16567                     if(forceAll){
16568                         this.store.clearFilter();
16569                     }else{
16570                         
16571                         if(this.specialFilter){
16572                             this.fireEvent('specialfilter', this);
16573                             this.onLoad();
16574                             return;
16575                         }
16576                         
16577                         this.store.filter(this.displayField, q);
16578                     }
16579                     
16580                     this.store.fireEvent("datachanged", this.store);
16581                     
16582                     this.onLoad();
16583                     
16584                     
16585                 }else{
16586                     
16587                     this.store.baseParams[this.queryParam] = q;
16588                     
16589                     var options = {params : this.getParams(q)};
16590                     
16591                     if(this.loadNext){
16592                         options.add = true;
16593                         options.params.start = this.page * this.pageSize;
16594                     }
16595                     
16596                     this.store.load(options);
16597                     
16598                     /*
16599                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16600                      *  we should expand the list on onLoad
16601                      *  so command out it
16602                      */
16603 //                    this.expand();
16604                 }
16605             }else{
16606                 this.selectedIndex = -1;
16607                 this.onLoad();   
16608             }
16609         }
16610         
16611         this.loadNext = false;
16612     },
16613     
16614     // private
16615     getParams : function(q){
16616         var p = {};
16617         //p[this.queryParam] = q;
16618         
16619         if(this.pageSize){
16620             p.start = 0;
16621             p.limit = this.pageSize;
16622         }
16623         return p;
16624     },
16625
16626     /**
16627      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16628      */
16629     collapse : function(){
16630         if(!this.isExpanded()){
16631             return;
16632         }
16633         
16634         this.list.hide();
16635         
16636         this.hasFocus = false;
16637         
16638         if(this.tickable){
16639             this.okBtn.hide();
16640             this.cancelBtn.hide();
16641             this.trigger.show();
16642             
16643             if(this.editable){
16644                 this.tickableInputEl().dom.value = '';
16645                 this.tickableInputEl().blur();
16646             }
16647             
16648         }
16649         
16650         Roo.get(document).un('mousedown', this.collapseIf, this);
16651         Roo.get(document).un('mousewheel', this.collapseIf, this);
16652         if (!this.editable) {
16653             Roo.get(document).un('keydown', this.listKeyPress, this);
16654         }
16655         this.fireEvent('collapse', this);
16656         
16657         this.validate();
16658     },
16659
16660     // private
16661     collapseIf : function(e){
16662         var in_combo  = e.within(this.el);
16663         var in_list =  e.within(this.list);
16664         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16665         
16666         if (in_combo || in_list || is_list) {
16667             //e.stopPropagation();
16668             return;
16669         }
16670         
16671         if(this.tickable){
16672             this.onTickableFooterButtonClick(e, false, false);
16673         }
16674
16675         this.collapse();
16676         
16677     },
16678
16679     /**
16680      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16681      */
16682     expand : function(){
16683        
16684         if(this.isExpanded() || !this.hasFocus){
16685             return;
16686         }
16687         
16688         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16689         this.list.setWidth(lw);
16690         
16691         Roo.log('expand');
16692         
16693         this.list.show();
16694         
16695         this.restrictHeight();
16696         
16697         if(this.tickable){
16698             
16699             this.tickItems = Roo.apply([], this.item);
16700             
16701             this.okBtn.show();
16702             this.cancelBtn.show();
16703             this.trigger.hide();
16704             
16705             if(this.editable){
16706                 this.tickableInputEl().focus();
16707             }
16708             
16709         }
16710         
16711         Roo.get(document).on('mousedown', this.collapseIf, this);
16712         Roo.get(document).on('mousewheel', this.collapseIf, this);
16713         if (!this.editable) {
16714             Roo.get(document).on('keydown', this.listKeyPress, this);
16715         }
16716         
16717         this.fireEvent('expand', this);
16718     },
16719
16720     // private
16721     // Implements the default empty TriggerField.onTriggerClick function
16722     onTriggerClick : function(e)
16723     {
16724         Roo.log('trigger click');
16725         
16726         if(this.disabled || !this.triggerList){
16727             return;
16728         }
16729         
16730         this.page = 0;
16731         this.loadNext = false;
16732         
16733         if(this.isExpanded()){
16734             this.collapse();
16735             if (!this.blockFocus) {
16736                 this.inputEl().focus();
16737             }
16738             
16739         }else {
16740             this.hasFocus = true;
16741             if(this.triggerAction == 'all') {
16742                 this.doQuery(this.allQuery, true);
16743             } else {
16744                 this.doQuery(this.getRawValue());
16745             }
16746             if (!this.blockFocus) {
16747                 this.inputEl().focus();
16748             }
16749         }
16750     },
16751     
16752     onTickableTriggerClick : function(e)
16753     {
16754         if(this.disabled){
16755             return;
16756         }
16757         
16758         this.page = 0;
16759         this.loadNext = false;
16760         this.hasFocus = true;
16761         
16762         if(this.triggerAction == 'all') {
16763             this.doQuery(this.allQuery, true);
16764         } else {
16765             this.doQuery(this.getRawValue());
16766         }
16767     },
16768     
16769     onSearchFieldClick : function(e)
16770     {
16771         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16772             this.onTickableFooterButtonClick(e, false, false);
16773             return;
16774         }
16775         
16776         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16777             return;
16778         }
16779         
16780         this.page = 0;
16781         this.loadNext = false;
16782         this.hasFocus = true;
16783         
16784         if(this.triggerAction == 'all') {
16785             this.doQuery(this.allQuery, true);
16786         } else {
16787             this.doQuery(this.getRawValue());
16788         }
16789     },
16790     
16791     listKeyPress : function(e)
16792     {
16793         //Roo.log('listkeypress');
16794         // scroll to first matching element based on key pres..
16795         if (e.isSpecialKey()) {
16796             return false;
16797         }
16798         var k = String.fromCharCode(e.getKey()).toUpperCase();
16799         //Roo.log(k);
16800         var match  = false;
16801         var csel = this.view.getSelectedNodes();
16802         var cselitem = false;
16803         if (csel.length) {
16804             var ix = this.view.indexOf(csel[0]);
16805             cselitem  = this.store.getAt(ix);
16806             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16807                 cselitem = false;
16808             }
16809             
16810         }
16811         
16812         this.store.each(function(v) { 
16813             if (cselitem) {
16814                 // start at existing selection.
16815                 if (cselitem.id == v.id) {
16816                     cselitem = false;
16817                 }
16818                 return true;
16819             }
16820                 
16821             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16822                 match = this.store.indexOf(v);
16823                 return false;
16824             }
16825             return true;
16826         }, this);
16827         
16828         if (match === false) {
16829             return true; // no more action?
16830         }
16831         // scroll to?
16832         this.view.select(match);
16833         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16834         sn.scrollIntoView(sn.dom.parentNode, false);
16835     },
16836     
16837     onViewScroll : function(e, t){
16838         
16839         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
16840             return;
16841         }
16842         
16843         this.hasQuery = true;
16844         
16845         this.loading = this.list.select('.loading', true).first();
16846         
16847         if(this.loading === null){
16848             this.list.createChild({
16849                 tag: 'div',
16850                 cls: 'loading roo-select2-more-results roo-select2-active',
16851                 html: 'Loading more results...'
16852             });
16853             
16854             this.loading = this.list.select('.loading', true).first();
16855             
16856             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16857             
16858             this.loading.hide();
16859         }
16860         
16861         this.loading.show();
16862         
16863         var _combo = this;
16864         
16865         this.page++;
16866         this.loadNext = true;
16867         
16868         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16869         
16870         return;
16871     },
16872     
16873     addItem : function(o)
16874     {   
16875         var dv = ''; // display value
16876         
16877         if (this.displayField) {
16878             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16879         } else {
16880             // this is an error condition!!!
16881             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16882         }
16883         
16884         if(!dv.length){
16885             return;
16886         }
16887         
16888         var choice = this.choices.createChild({
16889             tag: 'li',
16890             cls: 'roo-select2-search-choice',
16891             cn: [
16892                 {
16893                     tag: 'div',
16894                     html: dv
16895                 },
16896                 {
16897                     tag: 'a',
16898                     href: '#',
16899                     cls: 'roo-select2-search-choice-close fa fa-times',
16900                     tabindex: '-1'
16901                 }
16902             ]
16903             
16904         }, this.searchField);
16905         
16906         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16907         
16908         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16909         
16910         this.item.push(o);
16911         
16912         this.lastData = o;
16913         
16914         this.syncValue();
16915         
16916         this.inputEl().dom.value = '';
16917         
16918         this.validate();
16919     },
16920     
16921     onRemoveItem : function(e, _self, o)
16922     {
16923         e.preventDefault();
16924         
16925         this.lastItem = Roo.apply([], this.item);
16926         
16927         var index = this.item.indexOf(o.data) * 1;
16928         
16929         if( index < 0){
16930             Roo.log('not this item?!');
16931             return;
16932         }
16933         
16934         this.item.splice(index, 1);
16935         o.item.remove();
16936         
16937         this.syncValue();
16938         
16939         this.fireEvent('remove', this, e);
16940         
16941         this.validate();
16942         
16943     },
16944     
16945     syncValue : function()
16946     {
16947         if(!this.item.length){
16948             this.clearValue();
16949             return;
16950         }
16951             
16952         var value = [];
16953         var _this = this;
16954         Roo.each(this.item, function(i){
16955             if(_this.valueField){
16956                 value.push(i[_this.valueField]);
16957                 return;
16958             }
16959
16960             value.push(i);
16961         });
16962
16963         this.value = value.join(',');
16964
16965         if(this.hiddenField){
16966             this.hiddenField.dom.value = this.value;
16967         }
16968         
16969         this.store.fireEvent("datachanged", this.store);
16970         
16971         this.validate();
16972     },
16973     
16974     clearItem : function()
16975     {
16976         if(!this.multiple){
16977             return;
16978         }
16979         
16980         this.item = [];
16981         
16982         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16983            c.remove();
16984         });
16985         
16986         this.syncValue();
16987         
16988         this.validate();
16989         
16990         if(this.tickable && !Roo.isTouch){
16991             this.view.refresh();
16992         }
16993     },
16994     
16995     inputEl: function ()
16996     {
16997         if(Roo.isIOS && this.useNativeIOS){
16998             return this.el.select('select.roo-ios-select', true).first();
16999         }
17000         
17001         if(Roo.isTouch && this.mobileTouchView){
17002             return this.el.select('input.form-control',true).first();
17003         }
17004         
17005         if(this.tickable){
17006             return this.searchField;
17007         }
17008         
17009         return this.el.select('input.form-control',true).first();
17010     },
17011     
17012     onTickableFooterButtonClick : function(e, btn, el)
17013     {
17014         e.preventDefault();
17015         
17016         this.lastItem = Roo.apply([], this.item);
17017         
17018         if(btn && btn.name == 'cancel'){
17019             this.tickItems = Roo.apply([], this.item);
17020             this.collapse();
17021             return;
17022         }
17023         
17024         this.clearItem();
17025         
17026         var _this = this;
17027         
17028         Roo.each(this.tickItems, function(o){
17029             _this.addItem(o);
17030         });
17031         
17032         this.collapse();
17033         
17034     },
17035     
17036     validate : function()
17037     {
17038         if(this.getVisibilityEl().hasClass('hidden')){
17039             return true;
17040         }
17041         
17042         var v = this.getRawValue();
17043         
17044         if(this.multiple){
17045             v = this.getValue();
17046         }
17047         
17048         if(this.disabled || this.allowBlank || v.length){
17049             this.markValid();
17050             return true;
17051         }
17052         
17053         this.markInvalid();
17054         return false;
17055     },
17056     
17057     tickableInputEl : function()
17058     {
17059         if(!this.tickable || !this.editable){
17060             return this.inputEl();
17061         }
17062         
17063         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17064     },
17065     
17066     
17067     getAutoCreateTouchView : function()
17068     {
17069         var id = Roo.id();
17070         
17071         var cfg = {
17072             cls: 'form-group' //input-group
17073         };
17074         
17075         var input =  {
17076             tag: 'input',
17077             id : id,
17078             type : this.inputType,
17079             cls : 'form-control x-combo-noedit',
17080             autocomplete: 'new-password',
17081             placeholder : this.placeholder || '',
17082             readonly : true
17083         };
17084         
17085         if (this.name) {
17086             input.name = this.name;
17087         }
17088         
17089         if (this.size) {
17090             input.cls += ' input-' + this.size;
17091         }
17092         
17093         if (this.disabled) {
17094             input.disabled = true;
17095         }
17096         
17097         var inputblock = {
17098             cls : 'roo-combobox-wrap',
17099             cn : [
17100                 input
17101             ]
17102         };
17103         
17104         if(this.before){
17105             inputblock.cls += ' input-group';
17106             
17107             inputblock.cn.unshift({
17108                 tag :'span',
17109                 cls : 'input-group-addon input-group-prepend input-group-text',
17110                 html : this.before
17111             });
17112         }
17113         
17114         if(this.removable && !this.multiple){
17115             inputblock.cls += ' roo-removable';
17116             
17117             inputblock.cn.push({
17118                 tag: 'button',
17119                 html : 'x',
17120                 cls : 'roo-combo-removable-btn close'
17121             });
17122         }
17123
17124         if(this.hasFeedback && !this.allowBlank){
17125             
17126             inputblock.cls += ' has-feedback';
17127             
17128             inputblock.cn.push({
17129                 tag: 'span',
17130                 cls: 'glyphicon form-control-feedback'
17131             });
17132             
17133         }
17134         
17135         if (this.after) {
17136             
17137             inputblock.cls += (this.before) ? '' : ' input-group';
17138             
17139             inputblock.cn.push({
17140                 tag :'span',
17141                 cls : 'input-group-addon input-group-append input-group-text',
17142                 html : this.after
17143             });
17144         }
17145
17146         
17147         var ibwrap = inputblock;
17148         
17149         if(this.multiple){
17150             ibwrap = {
17151                 tag: 'ul',
17152                 cls: 'roo-select2-choices',
17153                 cn:[
17154                     {
17155                         tag: 'li',
17156                         cls: 'roo-select2-search-field',
17157                         cn: [
17158
17159                             inputblock
17160                         ]
17161                     }
17162                 ]
17163             };
17164         
17165             
17166         }
17167         
17168         var combobox = {
17169             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17170             cn: [
17171                 {
17172                     tag: 'input',
17173                     type : 'hidden',
17174                     cls: 'form-hidden-field'
17175                 },
17176                 ibwrap
17177             ]
17178         };
17179         
17180         if(!this.multiple && this.showToggleBtn){
17181             
17182             var caret = {
17183                 cls: 'caret'
17184             };
17185             
17186             if (this.caret != false) {
17187                 caret = {
17188                      tag: 'i',
17189                      cls: 'fa fa-' + this.caret
17190                 };
17191                 
17192             }
17193             
17194             combobox.cn.push({
17195                 tag :'span',
17196                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17197                 cn : [
17198                     Roo.bootstrap.version == 3 ? caret : '',
17199                     {
17200                         tag: 'span',
17201                         cls: 'combobox-clear',
17202                         cn  : [
17203                             {
17204                                 tag : 'i',
17205                                 cls: 'icon-remove'
17206                             }
17207                         ]
17208                     }
17209                 ]
17210
17211             })
17212         }
17213         
17214         if(this.multiple){
17215             combobox.cls += ' roo-select2-container-multi';
17216         }
17217         
17218         var align = this.labelAlign || this.parentLabelAlign();
17219         
17220         if (align ==='left' && this.fieldLabel.length) {
17221
17222             cfg.cn = [
17223                 {
17224                    tag : 'i',
17225                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17226                    tooltip : 'This field is required'
17227                 },
17228                 {
17229                     tag: 'label',
17230                     cls : 'control-label col-form-label',
17231                     html : this.fieldLabel
17232
17233                 },
17234                 {
17235                     cls : 'roo-combobox-wrap ', 
17236                     cn: [
17237                         combobox
17238                     ]
17239                 }
17240             ];
17241             
17242             var labelCfg = cfg.cn[1];
17243             var contentCfg = cfg.cn[2];
17244             
17245
17246             if(this.indicatorpos == 'right'){
17247                 cfg.cn = [
17248                     {
17249                         tag: 'label',
17250                         'for' :  id,
17251                         cls : 'control-label col-form-label',
17252                         cn : [
17253                             {
17254                                 tag : 'span',
17255                                 html : this.fieldLabel
17256                             },
17257                             {
17258                                 tag : 'i',
17259                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17260                                 tooltip : 'This field is required'
17261                             }
17262                         ]
17263                     },
17264                     {
17265                         cls : "roo-combobox-wrap ",
17266                         cn: [
17267                             combobox
17268                         ]
17269                     }
17270
17271                 ];
17272                 
17273                 labelCfg = cfg.cn[0];
17274                 contentCfg = cfg.cn[1];
17275             }
17276             
17277            
17278             
17279             if(this.labelWidth > 12){
17280                 labelCfg.style = "width: " + this.labelWidth + 'px';
17281             }
17282            
17283             if(this.labelWidth < 13 && this.labelmd == 0){
17284                 this.labelmd = this.labelWidth;
17285             }
17286             
17287             if(this.labellg > 0){
17288                 labelCfg.cls += ' col-lg-' + this.labellg;
17289                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17290             }
17291             
17292             if(this.labelmd > 0){
17293                 labelCfg.cls += ' col-md-' + this.labelmd;
17294                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17295             }
17296             
17297             if(this.labelsm > 0){
17298                 labelCfg.cls += ' col-sm-' + this.labelsm;
17299                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17300             }
17301             
17302             if(this.labelxs > 0){
17303                 labelCfg.cls += ' col-xs-' + this.labelxs;
17304                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17305             }
17306                 
17307                 
17308         } else if ( this.fieldLabel.length) {
17309             cfg.cn = [
17310                 {
17311                    tag : 'i',
17312                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17313                    tooltip : 'This field is required'
17314                 },
17315                 {
17316                     tag: 'label',
17317                     cls : 'control-label',
17318                     html : this.fieldLabel
17319
17320                 },
17321                 {
17322                     cls : '', 
17323                     cn: [
17324                         combobox
17325                     ]
17326                 }
17327             ];
17328             
17329             if(this.indicatorpos == 'right'){
17330                 cfg.cn = [
17331                     {
17332                         tag: 'label',
17333                         cls : 'control-label',
17334                         html : this.fieldLabel,
17335                         cn : [
17336                             {
17337                                tag : 'i',
17338                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17339                                tooltip : 'This field is required'
17340                             }
17341                         ]
17342                     },
17343                     {
17344                         cls : '', 
17345                         cn: [
17346                             combobox
17347                         ]
17348                     }
17349                 ];
17350             }
17351         } else {
17352             cfg.cn = combobox;    
17353         }
17354         
17355         
17356         var settings = this;
17357         
17358         ['xs','sm','md','lg'].map(function(size){
17359             if (settings[size]) {
17360                 cfg.cls += ' col-' + size + '-' + settings[size];
17361             }
17362         });
17363         
17364         return cfg;
17365     },
17366     
17367     initTouchView : function()
17368     {
17369         this.renderTouchView();
17370         
17371         this.touchViewEl.on('scroll', function(){
17372             this.el.dom.scrollTop = 0;
17373         }, this);
17374         
17375         this.originalValue = this.getValue();
17376         
17377         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17378         
17379         this.inputEl().on("click", this.showTouchView, this);
17380         if (this.triggerEl) {
17381             this.triggerEl.on("click", this.showTouchView, this);
17382         }
17383         
17384         
17385         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17386         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17387         
17388         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17389         
17390         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17391         this.store.on('load', this.onTouchViewLoad, this);
17392         this.store.on('loadexception', this.onTouchViewLoadException, this);
17393         
17394         if(this.hiddenName){
17395             
17396             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17397             
17398             this.hiddenField.dom.value =
17399                 this.hiddenValue !== undefined ? this.hiddenValue :
17400                 this.value !== undefined ? this.value : '';
17401         
17402             this.el.dom.removeAttribute('name');
17403             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17404         }
17405         
17406         if(this.multiple){
17407             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17408             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17409         }
17410         
17411         if(this.removable && !this.multiple){
17412             var close = this.closeTriggerEl();
17413             if(close){
17414                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17415                 close.on('click', this.removeBtnClick, this, close);
17416             }
17417         }
17418         /*
17419          * fix the bug in Safari iOS8
17420          */
17421         this.inputEl().on("focus", function(e){
17422             document.activeElement.blur();
17423         }, this);
17424         
17425         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17426         
17427         return;
17428         
17429         
17430     },
17431     
17432     renderTouchView : function()
17433     {
17434         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17435         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17436         
17437         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17438         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439         
17440         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17441         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17442         this.touchViewBodyEl.setStyle('overflow', 'auto');
17443         
17444         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17445         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17446         
17447         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17448         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17449         
17450     },
17451     
17452     showTouchView : function()
17453     {
17454         if(this.disabled){
17455             return;
17456         }
17457         
17458         this.touchViewHeaderEl.hide();
17459
17460         if(this.modalTitle.length){
17461             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17462             this.touchViewHeaderEl.show();
17463         }
17464
17465         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17466         this.touchViewEl.show();
17467
17468         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17469         
17470         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17471         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17472
17473         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17474
17475         if(this.modalTitle.length){
17476             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17477         }
17478         
17479         this.touchViewBodyEl.setHeight(bodyHeight);
17480
17481         if(this.animate){
17482             var _this = this;
17483             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17484         }else{
17485             this.touchViewEl.addClass(['in','show']);
17486         }
17487         
17488         if(this._touchViewMask){
17489             Roo.get(document.body).addClass("x-body-masked");
17490             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17491             this._touchViewMask.setStyle('z-index', 10000);
17492             this._touchViewMask.addClass('show');
17493         }
17494         
17495         this.doTouchViewQuery();
17496         
17497     },
17498     
17499     hideTouchView : function()
17500     {
17501         this.touchViewEl.removeClass(['in','show']);
17502
17503         if(this.animate){
17504             var _this = this;
17505             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17506         }else{
17507             this.touchViewEl.setStyle('display', 'none');
17508         }
17509         
17510         if(this._touchViewMask){
17511             this._touchViewMask.removeClass('show');
17512             Roo.get(document.body).removeClass("x-body-masked");
17513         }
17514     },
17515     
17516     setTouchViewValue : function()
17517     {
17518         if(this.multiple){
17519             this.clearItem();
17520         
17521             var _this = this;
17522
17523             Roo.each(this.tickItems, function(o){
17524                 this.addItem(o);
17525             }, this);
17526         }
17527         
17528         this.hideTouchView();
17529     },
17530     
17531     doTouchViewQuery : function()
17532     {
17533         var qe = {
17534             query: '',
17535             forceAll: true,
17536             combo: this,
17537             cancel:false
17538         };
17539         
17540         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17541             return false;
17542         }
17543         
17544         if(!this.alwaysQuery || this.mode == 'local'){
17545             this.onTouchViewLoad();
17546             return;
17547         }
17548         
17549         this.store.load();
17550     },
17551     
17552     onTouchViewBeforeLoad : function(combo,opts)
17553     {
17554         return;
17555     },
17556
17557     // private
17558     onTouchViewLoad : function()
17559     {
17560         if(this.store.getCount() < 1){
17561             this.onTouchViewEmptyResults();
17562             return;
17563         }
17564         
17565         this.clearTouchView();
17566         
17567         var rawValue = this.getRawValue();
17568         
17569         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17570         
17571         this.tickItems = [];
17572         
17573         this.store.data.each(function(d, rowIndex){
17574             var row = this.touchViewListGroup.createChild(template);
17575             
17576             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17577                 row.addClass(d.data.cls);
17578             }
17579             
17580             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17581                 var cfg = {
17582                     data : d.data,
17583                     html : d.data[this.displayField]
17584                 };
17585                 
17586                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17587                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17588                 }
17589             }
17590             row.removeClass('selected');
17591             if(!this.multiple && this.valueField &&
17592                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17593             {
17594                 // radio buttons..
17595                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17596                 row.addClass('selected');
17597             }
17598             
17599             if(this.multiple && this.valueField &&
17600                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17601             {
17602                 
17603                 // checkboxes...
17604                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17605                 this.tickItems.push(d.data);
17606             }
17607             
17608             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17609             
17610         }, this);
17611         
17612         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17613         
17614         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17615
17616         if(this.modalTitle.length){
17617             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17618         }
17619
17620         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17621         
17622         if(this.mobile_restrict_height && listHeight < bodyHeight){
17623             this.touchViewBodyEl.setHeight(listHeight);
17624         }
17625         
17626         var _this = this;
17627         
17628         if(firstChecked && listHeight > bodyHeight){
17629             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17630         }
17631         
17632     },
17633     
17634     onTouchViewLoadException : function()
17635     {
17636         this.hideTouchView();
17637     },
17638     
17639     onTouchViewEmptyResults : function()
17640     {
17641         this.clearTouchView();
17642         
17643         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17644         
17645         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17646         
17647     },
17648     
17649     clearTouchView : function()
17650     {
17651         this.touchViewListGroup.dom.innerHTML = '';
17652     },
17653     
17654     onTouchViewClick : function(e, el, o)
17655     {
17656         e.preventDefault();
17657         
17658         var row = o.row;
17659         var rowIndex = o.rowIndex;
17660         
17661         var r = this.store.getAt(rowIndex);
17662         
17663         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17664             
17665             if(!this.multiple){
17666                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17667                     c.dom.removeAttribute('checked');
17668                 }, this);
17669
17670                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17671
17672                 this.setFromData(r.data);
17673
17674                 var close = this.closeTriggerEl();
17675
17676                 if(close){
17677                     close.show();
17678                 }
17679
17680                 this.hideTouchView();
17681
17682                 this.fireEvent('select', this, r, rowIndex);
17683
17684                 return;
17685             }
17686
17687             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17688                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17689                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17690                 return;
17691             }
17692
17693             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17694             this.addItem(r.data);
17695             this.tickItems.push(r.data);
17696         }
17697     },
17698     
17699     getAutoCreateNativeIOS : function()
17700     {
17701         var cfg = {
17702             cls: 'form-group' //input-group,
17703         };
17704         
17705         var combobox =  {
17706             tag: 'select',
17707             cls : 'roo-ios-select'
17708         };
17709         
17710         if (this.name) {
17711             combobox.name = this.name;
17712         }
17713         
17714         if (this.disabled) {
17715             combobox.disabled = true;
17716         }
17717         
17718         var settings = this;
17719         
17720         ['xs','sm','md','lg'].map(function(size){
17721             if (settings[size]) {
17722                 cfg.cls += ' col-' + size + '-' + settings[size];
17723             }
17724         });
17725         
17726         cfg.cn = combobox;
17727         
17728         return cfg;
17729         
17730     },
17731     
17732     initIOSView : function()
17733     {
17734         this.store.on('load', this.onIOSViewLoad, this);
17735         
17736         return;
17737     },
17738     
17739     onIOSViewLoad : function()
17740     {
17741         if(this.store.getCount() < 1){
17742             return;
17743         }
17744         
17745         this.clearIOSView();
17746         
17747         if(this.allowBlank) {
17748             
17749             var default_text = '-- SELECT --';
17750             
17751             if(this.placeholder.length){
17752                 default_text = this.placeholder;
17753             }
17754             
17755             if(this.emptyTitle.length){
17756                 default_text += ' - ' + this.emptyTitle + ' -';
17757             }
17758             
17759             var opt = this.inputEl().createChild({
17760                 tag: 'option',
17761                 value : 0,
17762                 html : default_text
17763             });
17764             
17765             var o = {};
17766             o[this.valueField] = 0;
17767             o[this.displayField] = default_text;
17768             
17769             this.ios_options.push({
17770                 data : o,
17771                 el : opt
17772             });
17773             
17774         }
17775         
17776         this.store.data.each(function(d, rowIndex){
17777             
17778             var html = '';
17779             
17780             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17781                 html = d.data[this.displayField];
17782             }
17783             
17784             var value = '';
17785             
17786             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17787                 value = d.data[this.valueField];
17788             }
17789             
17790             var option = {
17791                 tag: 'option',
17792                 value : value,
17793                 html : html
17794             };
17795             
17796             if(this.value == d.data[this.valueField]){
17797                 option['selected'] = true;
17798             }
17799             
17800             var opt = this.inputEl().createChild(option);
17801             
17802             this.ios_options.push({
17803                 data : d.data,
17804                 el : opt
17805             });
17806             
17807         }, this);
17808         
17809         this.inputEl().on('change', function(){
17810            this.fireEvent('select', this);
17811         }, this);
17812         
17813     },
17814     
17815     clearIOSView: function()
17816     {
17817         this.inputEl().dom.innerHTML = '';
17818         
17819         this.ios_options = [];
17820     },
17821     
17822     setIOSValue: function(v)
17823     {
17824         this.value = v;
17825         
17826         if(!this.ios_options){
17827             return;
17828         }
17829         
17830         Roo.each(this.ios_options, function(opts){
17831            
17832            opts.el.dom.removeAttribute('selected');
17833            
17834            if(opts.data[this.valueField] != v){
17835                return;
17836            }
17837            
17838            opts.el.dom.setAttribute('selected', true);
17839            
17840         }, this);
17841     }
17842
17843     /** 
17844     * @cfg {Boolean} grow 
17845     * @hide 
17846     */
17847     /** 
17848     * @cfg {Number} growMin 
17849     * @hide 
17850     */
17851     /** 
17852     * @cfg {Number} growMax 
17853     * @hide 
17854     */
17855     /**
17856      * @hide
17857      * @method autoSize
17858      */
17859 });
17860
17861 Roo.apply(Roo.bootstrap.ComboBox,  {
17862     
17863     header : {
17864         tag: 'div',
17865         cls: 'modal-header',
17866         cn: [
17867             {
17868                 tag: 'h4',
17869                 cls: 'modal-title'
17870             }
17871         ]
17872     },
17873     
17874     body : {
17875         tag: 'div',
17876         cls: 'modal-body',
17877         cn: [
17878             {
17879                 tag: 'ul',
17880                 cls: 'list-group'
17881             }
17882         ]
17883     },
17884     
17885     listItemRadio : {
17886         tag: 'li',
17887         cls: 'list-group-item',
17888         cn: [
17889             {
17890                 tag: 'span',
17891                 cls: 'roo-combobox-list-group-item-value'
17892             },
17893             {
17894                 tag: 'div',
17895                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17896                 cn: [
17897                     {
17898                         tag: 'input',
17899                         type: 'radio'
17900                     },
17901                     {
17902                         tag: 'label'
17903                     }
17904                 ]
17905             }
17906         ]
17907     },
17908     
17909     listItemCheckbox : {
17910         tag: 'li',
17911         cls: 'list-group-item',
17912         cn: [
17913             {
17914                 tag: 'span',
17915                 cls: 'roo-combobox-list-group-item-value'
17916             },
17917             {
17918                 tag: 'div',
17919                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17920                 cn: [
17921                     {
17922                         tag: 'input',
17923                         type: 'checkbox'
17924                     },
17925                     {
17926                         tag: 'label'
17927                     }
17928                 ]
17929             }
17930         ]
17931     },
17932     
17933     emptyResult : {
17934         tag: 'div',
17935         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17936     },
17937     
17938     footer : {
17939         tag: 'div',
17940         cls: 'modal-footer',
17941         cn: [
17942             {
17943                 tag: 'div',
17944                 cls: 'row',
17945                 cn: [
17946                     {
17947                         tag: 'div',
17948                         cls: 'col-xs-6 text-left',
17949                         cn: {
17950                             tag: 'button',
17951                             cls: 'btn btn-danger roo-touch-view-cancel',
17952                             html: 'Cancel'
17953                         }
17954                     },
17955                     {
17956                         tag: 'div',
17957                         cls: 'col-xs-6 text-right',
17958                         cn: {
17959                             tag: 'button',
17960                             cls: 'btn btn-success roo-touch-view-ok',
17961                             html: 'OK'
17962                         }
17963                     }
17964                 ]
17965             }
17966         ]
17967         
17968     }
17969 });
17970
17971 Roo.apply(Roo.bootstrap.ComboBox,  {
17972     
17973     touchViewTemplate : {
17974         tag: 'div',
17975         cls: 'modal fade roo-combobox-touch-view',
17976         cn: [
17977             {
17978                 tag: 'div',
17979                 cls: 'modal-dialog',
17980                 style : 'position:fixed', // we have to fix position....
17981                 cn: [
17982                     {
17983                         tag: 'div',
17984                         cls: 'modal-content',
17985                         cn: [
17986                             Roo.bootstrap.ComboBox.header,
17987                             Roo.bootstrap.ComboBox.body,
17988                             Roo.bootstrap.ComboBox.footer
17989                         ]
17990                     }
17991                 ]
17992             }
17993         ]
17994     }
17995 });/*
17996  * Based on:
17997  * Ext JS Library 1.1.1
17998  * Copyright(c) 2006-2007, Ext JS, LLC.
17999  *
18000  * Originally Released Under LGPL - original licence link has changed is not relivant.
18001  *
18002  * Fork - LGPL
18003  * <script type="text/javascript">
18004  */
18005
18006 /**
18007  * @class Roo.View
18008  * @extends Roo.util.Observable
18009  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18010  * This class also supports single and multi selection modes. <br>
18011  * Create a data model bound view:
18012  <pre><code>
18013  var store = new Roo.data.Store(...);
18014
18015  var view = new Roo.View({
18016     el : "my-element",
18017     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18018  
18019     singleSelect: true,
18020     selectedClass: "ydataview-selected",
18021     store: store
18022  });
18023
18024  // listen for node click?
18025  view.on("click", function(vw, index, node, e){
18026  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18027  });
18028
18029  // load XML data
18030  dataModel.load("foobar.xml");
18031  </code></pre>
18032  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18033  * <br><br>
18034  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18035  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18036  * 
18037  * Note: old style constructor is still suported (container, template, config)
18038  * 
18039  * @constructor
18040  * Create a new View
18041  * @param {Object} config The config object
18042  * 
18043  */
18044 Roo.View = function(config, depreciated_tpl, depreciated_config){
18045     
18046     this.parent = false;
18047     
18048     if (typeof(depreciated_tpl) == 'undefined') {
18049         // new way.. - universal constructor.
18050         Roo.apply(this, config);
18051         this.el  = Roo.get(this.el);
18052     } else {
18053         // old format..
18054         this.el  = Roo.get(config);
18055         this.tpl = depreciated_tpl;
18056         Roo.apply(this, depreciated_config);
18057     }
18058     this.wrapEl  = this.el.wrap().wrap();
18059     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18060     
18061     
18062     if(typeof(this.tpl) == "string"){
18063         this.tpl = new Roo.Template(this.tpl);
18064     } else {
18065         // support xtype ctors..
18066         this.tpl = new Roo.factory(this.tpl, Roo);
18067     }
18068     
18069     
18070     this.tpl.compile();
18071     
18072     /** @private */
18073     this.addEvents({
18074         /**
18075          * @event beforeclick
18076          * Fires before a click is processed. Returns false to cancel the default action.
18077          * @param {Roo.View} this
18078          * @param {Number} index The index of the target node
18079          * @param {HTMLElement} node The target node
18080          * @param {Roo.EventObject} e The raw event object
18081          */
18082             "beforeclick" : true,
18083         /**
18084          * @event click
18085          * Fires when a template node is clicked.
18086          * @param {Roo.View} this
18087          * @param {Number} index The index of the target node
18088          * @param {HTMLElement} node The target node
18089          * @param {Roo.EventObject} e The raw event object
18090          */
18091             "click" : true,
18092         /**
18093          * @event dblclick
18094          * Fires when a template node is double clicked.
18095          * @param {Roo.View} this
18096          * @param {Number} index The index of the target node
18097          * @param {HTMLElement} node The target node
18098          * @param {Roo.EventObject} e The raw event object
18099          */
18100             "dblclick" : true,
18101         /**
18102          * @event contextmenu
18103          * Fires when a template node is right clicked.
18104          * @param {Roo.View} this
18105          * @param {Number} index The index of the target node
18106          * @param {HTMLElement} node The target node
18107          * @param {Roo.EventObject} e The raw event object
18108          */
18109             "contextmenu" : true,
18110         /**
18111          * @event selectionchange
18112          * Fires when the selected nodes change.
18113          * @param {Roo.View} this
18114          * @param {Array} selections Array of the selected nodes
18115          */
18116             "selectionchange" : true,
18117     
18118         /**
18119          * @event beforeselect
18120          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18121          * @param {Roo.View} this
18122          * @param {HTMLElement} node The node to be selected
18123          * @param {Array} selections Array of currently selected nodes
18124          */
18125             "beforeselect" : true,
18126         /**
18127          * @event preparedata
18128          * Fires on every row to render, to allow you to change the data.
18129          * @param {Roo.View} this
18130          * @param {Object} data to be rendered (change this)
18131          */
18132           "preparedata" : true
18133           
18134           
18135         });
18136
18137
18138
18139     this.el.on({
18140         "click": this.onClick,
18141         "dblclick": this.onDblClick,
18142         "contextmenu": this.onContextMenu,
18143         scope:this
18144     });
18145
18146     this.selections = [];
18147     this.nodes = [];
18148     this.cmp = new Roo.CompositeElementLite([]);
18149     if(this.store){
18150         this.store = Roo.factory(this.store, Roo.data);
18151         this.setStore(this.store, true);
18152     }
18153     
18154     if ( this.footer && this.footer.xtype) {
18155            
18156          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18157         
18158         this.footer.dataSource = this.store;
18159         this.footer.container = fctr;
18160         this.footer = Roo.factory(this.footer, Roo);
18161         fctr.insertFirst(this.el);
18162         
18163         // this is a bit insane - as the paging toolbar seems to detach the el..
18164 //        dom.parentNode.parentNode.parentNode
18165          // they get detached?
18166     }
18167     
18168     
18169     Roo.View.superclass.constructor.call(this);
18170     
18171     
18172 };
18173
18174 Roo.extend(Roo.View, Roo.util.Observable, {
18175     
18176      /**
18177      * @cfg {Roo.data.Store} store Data store to load data from.
18178      */
18179     store : false,
18180     
18181     /**
18182      * @cfg {String|Roo.Element} el The container element.
18183      */
18184     el : '',
18185     
18186     /**
18187      * @cfg {String|Roo.Template} tpl The template used by this View 
18188      */
18189     tpl : false,
18190     /**
18191      * @cfg {String} dataName the named area of the template to use as the data area
18192      *                          Works with domtemplates roo-name="name"
18193      */
18194     dataName: false,
18195     /**
18196      * @cfg {String} selectedClass The css class to add to selected nodes
18197      */
18198     selectedClass : "x-view-selected",
18199      /**
18200      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18201      */
18202     emptyText : "",
18203     
18204     /**
18205      * @cfg {String} text to display on mask (default Loading)
18206      */
18207     mask : false,
18208     /**
18209      * @cfg {Boolean} multiSelect Allow multiple selection
18210      */
18211     multiSelect : false,
18212     /**
18213      * @cfg {Boolean} singleSelect Allow single selection
18214      */
18215     singleSelect:  false,
18216     
18217     /**
18218      * @cfg {Boolean} toggleSelect - selecting 
18219      */
18220     toggleSelect : false,
18221     
18222     /**
18223      * @cfg {Boolean} tickable - selecting 
18224      */
18225     tickable : false,
18226     
18227     /**
18228      * Returns the element this view is bound to.
18229      * @return {Roo.Element}
18230      */
18231     getEl : function(){
18232         return this.wrapEl;
18233     },
18234     
18235     
18236
18237     /**
18238      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18239      */
18240     refresh : function(){
18241         //Roo.log('refresh');
18242         var t = this.tpl;
18243         
18244         // if we are using something like 'domtemplate', then
18245         // the what gets used is:
18246         // t.applySubtemplate(NAME, data, wrapping data..)
18247         // the outer template then get' applied with
18248         //     the store 'extra data'
18249         // and the body get's added to the
18250         //      roo-name="data" node?
18251         //      <span class='roo-tpl-{name}'></span> ?????
18252         
18253         
18254         
18255         this.clearSelections();
18256         this.el.update("");
18257         var html = [];
18258         var records = this.store.getRange();
18259         if(records.length < 1) {
18260             
18261             // is this valid??  = should it render a template??
18262             
18263             this.el.update(this.emptyText);
18264             return;
18265         }
18266         var el = this.el;
18267         if (this.dataName) {
18268             this.el.update(t.apply(this.store.meta)); //????
18269             el = this.el.child('.roo-tpl-' + this.dataName);
18270         }
18271         
18272         for(var i = 0, len = records.length; i < len; i++){
18273             var data = this.prepareData(records[i].data, i, records[i]);
18274             this.fireEvent("preparedata", this, data, i, records[i]);
18275             
18276             var d = Roo.apply({}, data);
18277             
18278             if(this.tickable){
18279                 Roo.apply(d, {'roo-id' : Roo.id()});
18280                 
18281                 var _this = this;
18282             
18283                 Roo.each(this.parent.item, function(item){
18284                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18285                         return;
18286                     }
18287                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18288                 });
18289             }
18290             
18291             html[html.length] = Roo.util.Format.trim(
18292                 this.dataName ?
18293                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18294                     t.apply(d)
18295             );
18296         }
18297         
18298         
18299         
18300         el.update(html.join(""));
18301         this.nodes = el.dom.childNodes;
18302         this.updateIndexes(0);
18303     },
18304     
18305
18306     /**
18307      * Function to override to reformat the data that is sent to
18308      * the template for each node.
18309      * DEPRICATED - use the preparedata event handler.
18310      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18311      * a JSON object for an UpdateManager bound view).
18312      */
18313     prepareData : function(data, index, record)
18314     {
18315         this.fireEvent("preparedata", this, data, index, record);
18316         return data;
18317     },
18318
18319     onUpdate : function(ds, record){
18320         // Roo.log('on update');   
18321         this.clearSelections();
18322         var index = this.store.indexOf(record);
18323         var n = this.nodes[index];
18324         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18325         n.parentNode.removeChild(n);
18326         this.updateIndexes(index, index);
18327     },
18328
18329     
18330     
18331 // --------- FIXME     
18332     onAdd : function(ds, records, index)
18333     {
18334         //Roo.log(['on Add', ds, records, index] );        
18335         this.clearSelections();
18336         if(this.nodes.length == 0){
18337             this.refresh();
18338             return;
18339         }
18340         var n = this.nodes[index];
18341         for(var i = 0, len = records.length; i < len; i++){
18342             var d = this.prepareData(records[i].data, i, records[i]);
18343             if(n){
18344                 this.tpl.insertBefore(n, d);
18345             }else{
18346                 
18347                 this.tpl.append(this.el, d);
18348             }
18349         }
18350         this.updateIndexes(index);
18351     },
18352
18353     onRemove : function(ds, record, index){
18354        // Roo.log('onRemove');
18355         this.clearSelections();
18356         var el = this.dataName  ?
18357             this.el.child('.roo-tpl-' + this.dataName) :
18358             this.el; 
18359         
18360         el.dom.removeChild(this.nodes[index]);
18361         this.updateIndexes(index);
18362     },
18363
18364     /**
18365      * Refresh an individual node.
18366      * @param {Number} index
18367      */
18368     refreshNode : function(index){
18369         this.onUpdate(this.store, this.store.getAt(index));
18370     },
18371
18372     updateIndexes : function(startIndex, endIndex){
18373         var ns = this.nodes;
18374         startIndex = startIndex || 0;
18375         endIndex = endIndex || ns.length - 1;
18376         for(var i = startIndex; i <= endIndex; i++){
18377             ns[i].nodeIndex = i;
18378         }
18379     },
18380
18381     /**
18382      * Changes the data store this view uses and refresh the view.
18383      * @param {Store} store
18384      */
18385     setStore : function(store, initial){
18386         if(!initial && this.store){
18387             this.store.un("datachanged", this.refresh);
18388             this.store.un("add", this.onAdd);
18389             this.store.un("remove", this.onRemove);
18390             this.store.un("update", this.onUpdate);
18391             this.store.un("clear", this.refresh);
18392             this.store.un("beforeload", this.onBeforeLoad);
18393             this.store.un("load", this.onLoad);
18394             this.store.un("loadexception", this.onLoad);
18395         }
18396         if(store){
18397           
18398             store.on("datachanged", this.refresh, this);
18399             store.on("add", this.onAdd, this);
18400             store.on("remove", this.onRemove, this);
18401             store.on("update", this.onUpdate, this);
18402             store.on("clear", this.refresh, this);
18403             store.on("beforeload", this.onBeforeLoad, this);
18404             store.on("load", this.onLoad, this);
18405             store.on("loadexception", this.onLoad, this);
18406         }
18407         
18408         if(store){
18409             this.refresh();
18410         }
18411     },
18412     /**
18413      * onbeforeLoad - masks the loading area.
18414      *
18415      */
18416     onBeforeLoad : function(store,opts)
18417     {
18418          //Roo.log('onBeforeLoad');   
18419         if (!opts.add) {
18420             this.el.update("");
18421         }
18422         this.el.mask(this.mask ? this.mask : "Loading" ); 
18423     },
18424     onLoad : function ()
18425     {
18426         this.el.unmask();
18427     },
18428     
18429
18430     /**
18431      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18432      * @param {HTMLElement} node
18433      * @return {HTMLElement} The template node
18434      */
18435     findItemFromChild : function(node){
18436         var el = this.dataName  ?
18437             this.el.child('.roo-tpl-' + this.dataName,true) :
18438             this.el.dom; 
18439         
18440         if(!node || node.parentNode == el){
18441                     return node;
18442             }
18443             var p = node.parentNode;
18444             while(p && p != el){
18445             if(p.parentNode == el){
18446                 return p;
18447             }
18448             p = p.parentNode;
18449         }
18450             return null;
18451     },
18452
18453     /** @ignore */
18454     onClick : function(e){
18455         var item = this.findItemFromChild(e.getTarget());
18456         if(item){
18457             var index = this.indexOf(item);
18458             if(this.onItemClick(item, index, e) !== false){
18459                 this.fireEvent("click", this, index, item, e);
18460             }
18461         }else{
18462             this.clearSelections();
18463         }
18464     },
18465
18466     /** @ignore */
18467     onContextMenu : function(e){
18468         var item = this.findItemFromChild(e.getTarget());
18469         if(item){
18470             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18471         }
18472     },
18473
18474     /** @ignore */
18475     onDblClick : function(e){
18476         var item = this.findItemFromChild(e.getTarget());
18477         if(item){
18478             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18479         }
18480     },
18481
18482     onItemClick : function(item, index, e)
18483     {
18484         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18485             return false;
18486         }
18487         if (this.toggleSelect) {
18488             var m = this.isSelected(item) ? 'unselect' : 'select';
18489             //Roo.log(m);
18490             var _t = this;
18491             _t[m](item, true, false);
18492             return true;
18493         }
18494         if(this.multiSelect || this.singleSelect){
18495             if(this.multiSelect && e.shiftKey && this.lastSelection){
18496                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18497             }else{
18498                 this.select(item, this.multiSelect && e.ctrlKey);
18499                 this.lastSelection = item;
18500             }
18501             
18502             if(!this.tickable){
18503                 e.preventDefault();
18504             }
18505             
18506         }
18507         return true;
18508     },
18509
18510     /**
18511      * Get the number of selected nodes.
18512      * @return {Number}
18513      */
18514     getSelectionCount : function(){
18515         return this.selections.length;
18516     },
18517
18518     /**
18519      * Get the currently selected nodes.
18520      * @return {Array} An array of HTMLElements
18521      */
18522     getSelectedNodes : function(){
18523         return this.selections;
18524     },
18525
18526     /**
18527      * Get the indexes of the selected nodes.
18528      * @return {Array}
18529      */
18530     getSelectedIndexes : function(){
18531         var indexes = [], s = this.selections;
18532         for(var i = 0, len = s.length; i < len; i++){
18533             indexes.push(s[i].nodeIndex);
18534         }
18535         return indexes;
18536     },
18537
18538     /**
18539      * Clear all selections
18540      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18541      */
18542     clearSelections : function(suppressEvent){
18543         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18544             this.cmp.elements = this.selections;
18545             this.cmp.removeClass(this.selectedClass);
18546             this.selections = [];
18547             if(!suppressEvent){
18548                 this.fireEvent("selectionchange", this, this.selections);
18549             }
18550         }
18551     },
18552
18553     /**
18554      * Returns true if the passed node is selected
18555      * @param {HTMLElement/Number} node The node or node index
18556      * @return {Boolean}
18557      */
18558     isSelected : function(node){
18559         var s = this.selections;
18560         if(s.length < 1){
18561             return false;
18562         }
18563         node = this.getNode(node);
18564         return s.indexOf(node) !== -1;
18565     },
18566
18567     /**
18568      * Selects nodes.
18569      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18570      * @param {Boolean} keepExisting (optional) true to keep existing selections
18571      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18572      */
18573     select : function(nodeInfo, keepExisting, suppressEvent){
18574         if(nodeInfo instanceof Array){
18575             if(!keepExisting){
18576                 this.clearSelections(true);
18577             }
18578             for(var i = 0, len = nodeInfo.length; i < len; i++){
18579                 this.select(nodeInfo[i], true, true);
18580             }
18581             return;
18582         } 
18583         var node = this.getNode(nodeInfo);
18584         if(!node || this.isSelected(node)){
18585             return; // already selected.
18586         }
18587         if(!keepExisting){
18588             this.clearSelections(true);
18589         }
18590         
18591         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18592             Roo.fly(node).addClass(this.selectedClass);
18593             this.selections.push(node);
18594             if(!suppressEvent){
18595                 this.fireEvent("selectionchange", this, this.selections);
18596             }
18597         }
18598         
18599         
18600     },
18601       /**
18602      * Unselects nodes.
18603      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18604      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18605      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18606      */
18607     unselect : function(nodeInfo, keepExisting, suppressEvent)
18608     {
18609         if(nodeInfo instanceof Array){
18610             Roo.each(this.selections, function(s) {
18611                 this.unselect(s, nodeInfo);
18612             }, this);
18613             return;
18614         }
18615         var node = this.getNode(nodeInfo);
18616         if(!node || !this.isSelected(node)){
18617             //Roo.log("not selected");
18618             return; // not selected.
18619         }
18620         // fireevent???
18621         var ns = [];
18622         Roo.each(this.selections, function(s) {
18623             if (s == node ) {
18624                 Roo.fly(node).removeClass(this.selectedClass);
18625
18626                 return;
18627             }
18628             ns.push(s);
18629         },this);
18630         
18631         this.selections= ns;
18632         this.fireEvent("selectionchange", this, this.selections);
18633     },
18634
18635     /**
18636      * Gets a template node.
18637      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18638      * @return {HTMLElement} The node or null if it wasn't found
18639      */
18640     getNode : function(nodeInfo){
18641         if(typeof nodeInfo == "string"){
18642             return document.getElementById(nodeInfo);
18643         }else if(typeof nodeInfo == "number"){
18644             return this.nodes[nodeInfo];
18645         }
18646         return nodeInfo;
18647     },
18648
18649     /**
18650      * Gets a range template nodes.
18651      * @param {Number} startIndex
18652      * @param {Number} endIndex
18653      * @return {Array} An array of nodes
18654      */
18655     getNodes : function(start, end){
18656         var ns = this.nodes;
18657         start = start || 0;
18658         end = typeof end == "undefined" ? ns.length - 1 : end;
18659         var nodes = [];
18660         if(start <= end){
18661             for(var i = start; i <= end; i++){
18662                 nodes.push(ns[i]);
18663             }
18664         } else{
18665             for(var i = start; i >= end; i--){
18666                 nodes.push(ns[i]);
18667             }
18668         }
18669         return nodes;
18670     },
18671
18672     /**
18673      * Finds the index of the passed node
18674      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18675      * @return {Number} The index of the node or -1
18676      */
18677     indexOf : function(node){
18678         node = this.getNode(node);
18679         if(typeof node.nodeIndex == "number"){
18680             return node.nodeIndex;
18681         }
18682         var ns = this.nodes;
18683         for(var i = 0, len = ns.length; i < len; i++){
18684             if(ns[i] == node){
18685                 return i;
18686             }
18687         }
18688         return -1;
18689     }
18690 });
18691 /*
18692  * - LGPL
18693  *
18694  * based on jquery fullcalendar
18695  * 
18696  */
18697
18698 Roo.bootstrap = Roo.bootstrap || {};
18699 /**
18700  * @class Roo.bootstrap.Calendar
18701  * @extends Roo.bootstrap.Component
18702  * Bootstrap Calendar class
18703  * @cfg {Boolean} loadMask (true|false) default false
18704  * @cfg {Object} header generate the user specific header of the calendar, default false
18705
18706  * @constructor
18707  * Create a new Container
18708  * @param {Object} config The config object
18709  */
18710
18711
18712
18713 Roo.bootstrap.Calendar = function(config){
18714     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18715      this.addEvents({
18716         /**
18717              * @event select
18718              * Fires when a date is selected
18719              * @param {DatePicker} this
18720              * @param {Date} date The selected date
18721              */
18722         'select': true,
18723         /**
18724              * @event monthchange
18725              * Fires when the displayed month changes 
18726              * @param {DatePicker} this
18727              * @param {Date} date The selected month
18728              */
18729         'monthchange': true,
18730         /**
18731              * @event evententer
18732              * Fires when mouse over an event
18733              * @param {Calendar} this
18734              * @param {event} Event
18735              */
18736         'evententer': true,
18737         /**
18738              * @event eventleave
18739              * Fires when the mouse leaves an
18740              * @param {Calendar} this
18741              * @param {event}
18742              */
18743         'eventleave': true,
18744         /**
18745              * @event eventclick
18746              * Fires when the mouse click an
18747              * @param {Calendar} this
18748              * @param {event}
18749              */
18750         'eventclick': true
18751         
18752     });
18753
18754 };
18755
18756 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18757     
18758      /**
18759      * @cfg {Number} startDay
18760      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18761      */
18762     startDay : 0,
18763     
18764     loadMask : false,
18765     
18766     header : false,
18767       
18768     getAutoCreate : function(){
18769         
18770         
18771         var fc_button = function(name, corner, style, content ) {
18772             return Roo.apply({},{
18773                 tag : 'span',
18774                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18775                          (corner.length ?
18776                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18777                             ''
18778                         ),
18779                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18780                 unselectable: 'on'
18781             });
18782         };
18783         
18784         var header = {};
18785         
18786         if(!this.header){
18787             header = {
18788                 tag : 'table',
18789                 cls : 'fc-header',
18790                 style : 'width:100%',
18791                 cn : [
18792                     {
18793                         tag: 'tr',
18794                         cn : [
18795                             {
18796                                 tag : 'td',
18797                                 cls : 'fc-header-left',
18798                                 cn : [
18799                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18800                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18801                                     { tag: 'span', cls: 'fc-header-space' },
18802                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18803
18804
18805                                 ]
18806                             },
18807
18808                             {
18809                                 tag : 'td',
18810                                 cls : 'fc-header-center',
18811                                 cn : [
18812                                     {
18813                                         tag: 'span',
18814                                         cls: 'fc-header-title',
18815                                         cn : {
18816                                             tag: 'H2',
18817                                             html : 'month / year'
18818                                         }
18819                                     }
18820
18821                                 ]
18822                             },
18823                             {
18824                                 tag : 'td',
18825                                 cls : 'fc-header-right',
18826                                 cn : [
18827                               /*      fc_button('month', 'left', '', 'month' ),
18828                                     fc_button('week', '', '', 'week' ),
18829                                     fc_button('day', 'right', '', 'day' )
18830                                 */    
18831
18832                                 ]
18833                             }
18834
18835                         ]
18836                     }
18837                 ]
18838             };
18839         }
18840         
18841         header = this.header;
18842         
18843        
18844         var cal_heads = function() {
18845             var ret = [];
18846             // fixme - handle this.
18847             
18848             for (var i =0; i < Date.dayNames.length; i++) {
18849                 var d = Date.dayNames[i];
18850                 ret.push({
18851                     tag: 'th',
18852                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18853                     html : d.substring(0,3)
18854                 });
18855                 
18856             }
18857             ret[0].cls += ' fc-first';
18858             ret[6].cls += ' fc-last';
18859             return ret;
18860         };
18861         var cal_cell = function(n) {
18862             return  {
18863                 tag: 'td',
18864                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18865                 cn : [
18866                     {
18867                         cn : [
18868                             {
18869                                 cls: 'fc-day-number',
18870                                 html: 'D'
18871                             },
18872                             {
18873                                 cls: 'fc-day-content',
18874                              
18875                                 cn : [
18876                                      {
18877                                         style: 'position: relative;' // height: 17px;
18878                                     }
18879                                 ]
18880                             }
18881                             
18882                             
18883                         ]
18884                     }
18885                 ]
18886                 
18887             }
18888         };
18889         var cal_rows = function() {
18890             
18891             var ret = [];
18892             for (var r = 0; r < 6; r++) {
18893                 var row= {
18894                     tag : 'tr',
18895                     cls : 'fc-week',
18896                     cn : []
18897                 };
18898                 
18899                 for (var i =0; i < Date.dayNames.length; i++) {
18900                     var d = Date.dayNames[i];
18901                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18902
18903                 }
18904                 row.cn[0].cls+=' fc-first';
18905                 row.cn[0].cn[0].style = 'min-height:90px';
18906                 row.cn[6].cls+=' fc-last';
18907                 ret.push(row);
18908                 
18909             }
18910             ret[0].cls += ' fc-first';
18911             ret[4].cls += ' fc-prev-last';
18912             ret[5].cls += ' fc-last';
18913             return ret;
18914             
18915         };
18916         
18917         var cal_table = {
18918             tag: 'table',
18919             cls: 'fc-border-separate',
18920             style : 'width:100%',
18921             cellspacing  : 0,
18922             cn : [
18923                 { 
18924                     tag: 'thead',
18925                     cn : [
18926                         { 
18927                             tag: 'tr',
18928                             cls : 'fc-first fc-last',
18929                             cn : cal_heads()
18930                         }
18931                     ]
18932                 },
18933                 { 
18934                     tag: 'tbody',
18935                     cn : cal_rows()
18936                 }
18937                   
18938             ]
18939         };
18940          
18941          var cfg = {
18942             cls : 'fc fc-ltr',
18943             cn : [
18944                 header,
18945                 {
18946                     cls : 'fc-content',
18947                     style : "position: relative;",
18948                     cn : [
18949                         {
18950                             cls : 'fc-view fc-view-month fc-grid',
18951                             style : 'position: relative',
18952                             unselectable : 'on',
18953                             cn : [
18954                                 {
18955                                     cls : 'fc-event-container',
18956                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18957                                 },
18958                                 cal_table
18959                             ]
18960                         }
18961                     ]
18962     
18963                 }
18964            ] 
18965             
18966         };
18967         
18968          
18969         
18970         return cfg;
18971     },
18972     
18973     
18974     initEvents : function()
18975     {
18976         if(!this.store){
18977             throw "can not find store for calendar";
18978         }
18979         
18980         var mark = {
18981             tag: "div",
18982             cls:"x-dlg-mask",
18983             style: "text-align:center",
18984             cn: [
18985                 {
18986                     tag: "div",
18987                     style: "background-color:white;width:50%;margin:250 auto",
18988                     cn: [
18989                         {
18990                             tag: "img",
18991                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18992                         },
18993                         {
18994                             tag: "span",
18995                             html: "Loading"
18996                         }
18997                         
18998                     ]
18999                 }
19000             ]
19001         };
19002         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19003         
19004         var size = this.el.select('.fc-content', true).first().getSize();
19005         this.maskEl.setSize(size.width, size.height);
19006         this.maskEl.enableDisplayMode("block");
19007         if(!this.loadMask){
19008             this.maskEl.hide();
19009         }
19010         
19011         this.store = Roo.factory(this.store, Roo.data);
19012         this.store.on('load', this.onLoad, this);
19013         this.store.on('beforeload', this.onBeforeLoad, this);
19014         
19015         this.resize();
19016         
19017         this.cells = this.el.select('.fc-day',true);
19018         //Roo.log(this.cells);
19019         this.textNodes = this.el.query('.fc-day-number');
19020         this.cells.addClassOnOver('fc-state-hover');
19021         
19022         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19023         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19024         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19025         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19026         
19027         this.on('monthchange', this.onMonthChange, this);
19028         
19029         this.update(new Date().clearTime());
19030     },
19031     
19032     resize : function() {
19033         var sz  = this.el.getSize();
19034         
19035         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19036         this.el.select('.fc-day-content div',true).setHeight(34);
19037     },
19038     
19039     
19040     // private
19041     showPrevMonth : function(e){
19042         this.update(this.activeDate.add("mo", -1));
19043     },
19044     showToday : function(e){
19045         this.update(new Date().clearTime());
19046     },
19047     // private
19048     showNextMonth : function(e){
19049         this.update(this.activeDate.add("mo", 1));
19050     },
19051
19052     // private
19053     showPrevYear : function(){
19054         this.update(this.activeDate.add("y", -1));
19055     },
19056
19057     // private
19058     showNextYear : function(){
19059         this.update(this.activeDate.add("y", 1));
19060     },
19061
19062     
19063    // private
19064     update : function(date)
19065     {
19066         var vd = this.activeDate;
19067         this.activeDate = date;
19068 //        if(vd && this.el){
19069 //            var t = date.getTime();
19070 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19071 //                Roo.log('using add remove');
19072 //                
19073 //                this.fireEvent('monthchange', this, date);
19074 //                
19075 //                this.cells.removeClass("fc-state-highlight");
19076 //                this.cells.each(function(c){
19077 //                   if(c.dateValue == t){
19078 //                       c.addClass("fc-state-highlight");
19079 //                       setTimeout(function(){
19080 //                            try{c.dom.firstChild.focus();}catch(e){}
19081 //                       }, 50);
19082 //                       return false;
19083 //                   }
19084 //                   return true;
19085 //                });
19086 //                return;
19087 //            }
19088 //        }
19089         
19090         var days = date.getDaysInMonth();
19091         
19092         var firstOfMonth = date.getFirstDateOfMonth();
19093         var startingPos = firstOfMonth.getDay()-this.startDay;
19094         
19095         if(startingPos < this.startDay){
19096             startingPos += 7;
19097         }
19098         
19099         var pm = date.add(Date.MONTH, -1);
19100         var prevStart = pm.getDaysInMonth()-startingPos;
19101 //        
19102         this.cells = this.el.select('.fc-day',true);
19103         this.textNodes = this.el.query('.fc-day-number');
19104         this.cells.addClassOnOver('fc-state-hover');
19105         
19106         var cells = this.cells.elements;
19107         var textEls = this.textNodes;
19108         
19109         Roo.each(cells, function(cell){
19110             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19111         });
19112         
19113         days += startingPos;
19114
19115         // convert everything to numbers so it's fast
19116         var day = 86400000;
19117         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19118         //Roo.log(d);
19119         //Roo.log(pm);
19120         //Roo.log(prevStart);
19121         
19122         var today = new Date().clearTime().getTime();
19123         var sel = date.clearTime().getTime();
19124         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19125         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19126         var ddMatch = this.disabledDatesRE;
19127         var ddText = this.disabledDatesText;
19128         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19129         var ddaysText = this.disabledDaysText;
19130         var format = this.format;
19131         
19132         var setCellClass = function(cal, cell){
19133             cell.row = 0;
19134             cell.events = [];
19135             cell.more = [];
19136             //Roo.log('set Cell Class');
19137             cell.title = "";
19138             var t = d.getTime();
19139             
19140             //Roo.log(d);
19141             
19142             cell.dateValue = t;
19143             if(t == today){
19144                 cell.className += " fc-today";
19145                 cell.className += " fc-state-highlight";
19146                 cell.title = cal.todayText;
19147             }
19148             if(t == sel){
19149                 // disable highlight in other month..
19150                 //cell.className += " fc-state-highlight";
19151                 
19152             }
19153             // disabling
19154             if(t < min) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.minText;
19157                 return;
19158             }
19159             if(t > max) {
19160                 cell.className = " fc-state-disabled";
19161                 cell.title = cal.maxText;
19162                 return;
19163             }
19164             if(ddays){
19165                 if(ddays.indexOf(d.getDay()) != -1){
19166                     cell.title = ddaysText;
19167                     cell.className = " fc-state-disabled";
19168                 }
19169             }
19170             if(ddMatch && format){
19171                 var fvalue = d.dateFormat(format);
19172                 if(ddMatch.test(fvalue)){
19173                     cell.title = ddText.replace("%0", fvalue);
19174                     cell.className = " fc-state-disabled";
19175                 }
19176             }
19177             
19178             if (!cell.initialClassName) {
19179                 cell.initialClassName = cell.dom.className;
19180             }
19181             
19182             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19183         };
19184
19185         var i = 0;
19186         
19187         for(; i < startingPos; i++) {
19188             textEls[i].innerHTML = (++prevStart);
19189             d.setDate(d.getDate()+1);
19190             
19191             cells[i].className = "fc-past fc-other-month";
19192             setCellClass(this, cells[i]);
19193         }
19194         
19195         var intDay = 0;
19196         
19197         for(; i < days; i++){
19198             intDay = i - startingPos + 1;
19199             textEls[i].innerHTML = (intDay);
19200             d.setDate(d.getDate()+1);
19201             
19202             cells[i].className = ''; // "x-date-active";
19203             setCellClass(this, cells[i]);
19204         }
19205         var extraDays = 0;
19206         
19207         for(; i < 42; i++) {
19208             textEls[i].innerHTML = (++extraDays);
19209             d.setDate(d.getDate()+1);
19210             
19211             cells[i].className = "fc-future fc-other-month";
19212             setCellClass(this, cells[i]);
19213         }
19214         
19215         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19216         
19217         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19218         
19219         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19220         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19221         
19222         if(totalRows != 6){
19223             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19224             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19225         }
19226         
19227         this.fireEvent('monthchange', this, date);
19228         
19229         
19230         /*
19231         if(!this.internalRender){
19232             var main = this.el.dom.firstChild;
19233             var w = main.offsetWidth;
19234             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19235             Roo.fly(main).setWidth(w);
19236             this.internalRender = true;
19237             // opera does not respect the auto grow header center column
19238             // then, after it gets a width opera refuses to recalculate
19239             // without a second pass
19240             if(Roo.isOpera && !this.secondPass){
19241                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19242                 this.secondPass = true;
19243                 this.update.defer(10, this, [date]);
19244             }
19245         }
19246         */
19247         
19248     },
19249     
19250     findCell : function(dt) {
19251         dt = dt.clearTime().getTime();
19252         var ret = false;
19253         this.cells.each(function(c){
19254             //Roo.log("check " +c.dateValue + '?=' + dt);
19255             if(c.dateValue == dt){
19256                 ret = c;
19257                 return false;
19258             }
19259             return true;
19260         });
19261         
19262         return ret;
19263     },
19264     
19265     findCells : function(ev) {
19266         var s = ev.start.clone().clearTime().getTime();
19267        // Roo.log(s);
19268         var e= ev.end.clone().clearTime().getTime();
19269        // Roo.log(e);
19270         var ret = [];
19271         this.cells.each(function(c){
19272              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19273             
19274             if(c.dateValue > e){
19275                 return ;
19276             }
19277             if(c.dateValue < s){
19278                 return ;
19279             }
19280             ret.push(c);
19281         });
19282         
19283         return ret;    
19284     },
19285     
19286 //    findBestRow: function(cells)
19287 //    {
19288 //        var ret = 0;
19289 //        
19290 //        for (var i =0 ; i < cells.length;i++) {
19291 //            ret  = Math.max(cells[i].rows || 0,ret);
19292 //        }
19293 //        return ret;
19294 //        
19295 //    },
19296     
19297     
19298     addItem : function(ev)
19299     {
19300         // look for vertical location slot in
19301         var cells = this.findCells(ev);
19302         
19303 //        ev.row = this.findBestRow(cells);
19304         
19305         // work out the location.
19306         
19307         var crow = false;
19308         var rows = [];
19309         for(var i =0; i < cells.length; i++) {
19310             
19311             cells[i].row = cells[0].row;
19312             
19313             if(i == 0){
19314                 cells[i].row = cells[i].row + 1;
19315             }
19316             
19317             if (!crow) {
19318                 crow = {
19319                     start : cells[i],
19320                     end :  cells[i]
19321                 };
19322                 continue;
19323             }
19324             if (crow.start.getY() == cells[i].getY()) {
19325                 // on same row.
19326                 crow.end = cells[i];
19327                 continue;
19328             }
19329             // different row.
19330             rows.push(crow);
19331             crow = {
19332                 start: cells[i],
19333                 end : cells[i]
19334             };
19335             
19336         }
19337         
19338         rows.push(crow);
19339         ev.els = [];
19340         ev.rows = rows;
19341         ev.cells = cells;
19342         
19343         cells[0].events.push(ev);
19344         
19345         this.calevents.push(ev);
19346     },
19347     
19348     clearEvents: function() {
19349         
19350         if(!this.calevents){
19351             return;
19352         }
19353         
19354         Roo.each(this.cells.elements, function(c){
19355             c.row = 0;
19356             c.events = [];
19357             c.more = [];
19358         });
19359         
19360         Roo.each(this.calevents, function(e) {
19361             Roo.each(e.els, function(el) {
19362                 el.un('mouseenter' ,this.onEventEnter, this);
19363                 el.un('mouseleave' ,this.onEventLeave, this);
19364                 el.remove();
19365             },this);
19366         },this);
19367         
19368         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19369             e.remove();
19370         });
19371         
19372     },
19373     
19374     renderEvents: function()
19375     {   
19376         var _this = this;
19377         
19378         this.cells.each(function(c) {
19379             
19380             if(c.row < 5){
19381                 return;
19382             }
19383             
19384             var ev = c.events;
19385             
19386             var r = 4;
19387             if(c.row != c.events.length){
19388                 r = 4 - (4 - (c.row - c.events.length));
19389             }
19390             
19391             c.events = ev.slice(0, r);
19392             c.more = ev.slice(r);
19393             
19394             if(c.more.length && c.more.length == 1){
19395                 c.events.push(c.more.pop());
19396             }
19397             
19398             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19399             
19400         });
19401             
19402         this.cells.each(function(c) {
19403             
19404             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19405             
19406             
19407             for (var e = 0; e < c.events.length; e++){
19408                 var ev = c.events[e];
19409                 var rows = ev.rows;
19410                 
19411                 for(var i = 0; i < rows.length; i++) {
19412                 
19413                     // how many rows should it span..
19414
19415                     var  cfg = {
19416                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19417                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19418
19419                         unselectable : "on",
19420                         cn : [
19421                             {
19422                                 cls: 'fc-event-inner',
19423                                 cn : [
19424     //                                {
19425     //                                  tag:'span',
19426     //                                  cls: 'fc-event-time',
19427     //                                  html : cells.length > 1 ? '' : ev.time
19428     //                                },
19429                                     {
19430                                       tag:'span',
19431                                       cls: 'fc-event-title',
19432                                       html : String.format('{0}', ev.title)
19433                                     }
19434
19435
19436                                 ]
19437                             },
19438                             {
19439                                 cls: 'ui-resizable-handle ui-resizable-e',
19440                                 html : '&nbsp;&nbsp;&nbsp'
19441                             }
19442
19443                         ]
19444                     };
19445
19446                     if (i == 0) {
19447                         cfg.cls += ' fc-event-start';
19448                     }
19449                     if ((i+1) == rows.length) {
19450                         cfg.cls += ' fc-event-end';
19451                     }
19452
19453                     var ctr = _this.el.select('.fc-event-container',true).first();
19454                     var cg = ctr.createChild(cfg);
19455
19456                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19457                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19458
19459                     var r = (c.more.length) ? 1 : 0;
19460                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19461                     cg.setWidth(ebox.right - sbox.x -2);
19462
19463                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19464                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19465                     cg.on('click', _this.onEventClick, _this, ev);
19466
19467                     ev.els.push(cg);
19468                     
19469                 }
19470                 
19471             }
19472             
19473             
19474             if(c.more.length){
19475                 var  cfg = {
19476                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19477                     style : 'position: absolute',
19478                     unselectable : "on",
19479                     cn : [
19480                         {
19481                             cls: 'fc-event-inner',
19482                             cn : [
19483                                 {
19484                                   tag:'span',
19485                                   cls: 'fc-event-title',
19486                                   html : 'More'
19487                                 }
19488
19489
19490                             ]
19491                         },
19492                         {
19493                             cls: 'ui-resizable-handle ui-resizable-e',
19494                             html : '&nbsp;&nbsp;&nbsp'
19495                         }
19496
19497                     ]
19498                 };
19499
19500                 var ctr = _this.el.select('.fc-event-container',true).first();
19501                 var cg = ctr.createChild(cfg);
19502
19503                 var sbox = c.select('.fc-day-content',true).first().getBox();
19504                 var ebox = c.select('.fc-day-content',true).first().getBox();
19505                 //Roo.log(cg);
19506                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19507                 cg.setWidth(ebox.right - sbox.x -2);
19508
19509                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19510                 
19511             }
19512             
19513         });
19514         
19515         
19516         
19517     },
19518     
19519     onEventEnter: function (e, el,event,d) {
19520         this.fireEvent('evententer', this, el, event);
19521     },
19522     
19523     onEventLeave: function (e, el,event,d) {
19524         this.fireEvent('eventleave', this, el, event);
19525     },
19526     
19527     onEventClick: function (e, el,event,d) {
19528         this.fireEvent('eventclick', this, el, event);
19529     },
19530     
19531     onMonthChange: function () {
19532         this.store.load();
19533     },
19534     
19535     onMoreEventClick: function(e, el, more)
19536     {
19537         var _this = this;
19538         
19539         this.calpopover.placement = 'right';
19540         this.calpopover.setTitle('More');
19541         
19542         this.calpopover.setContent('');
19543         
19544         var ctr = this.calpopover.el.select('.popover-content', true).first();
19545         
19546         Roo.each(more, function(m){
19547             var cfg = {
19548                 cls : 'fc-event-hori fc-event-draggable',
19549                 html : m.title
19550             };
19551             var cg = ctr.createChild(cfg);
19552             
19553             cg.on('click', _this.onEventClick, _this, m);
19554         });
19555         
19556         this.calpopover.show(el);
19557         
19558         
19559     },
19560     
19561     onLoad: function () 
19562     {   
19563         this.calevents = [];
19564         var cal = this;
19565         
19566         if(this.store.getCount() > 0){
19567             this.store.data.each(function(d){
19568                cal.addItem({
19569                     id : d.data.id,
19570                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19571                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19572                     time : d.data.start_time,
19573                     title : d.data.title,
19574                     description : d.data.description,
19575                     venue : d.data.venue
19576                 });
19577             });
19578         }
19579         
19580         this.renderEvents();
19581         
19582         if(this.calevents.length && this.loadMask){
19583             this.maskEl.hide();
19584         }
19585     },
19586     
19587     onBeforeLoad: function()
19588     {
19589         this.clearEvents();
19590         if(this.loadMask){
19591             this.maskEl.show();
19592         }
19593     }
19594 });
19595
19596  
19597  /*
19598  * - LGPL
19599  *
19600  * element
19601  * 
19602  */
19603
19604 /**
19605  * @class Roo.bootstrap.Popover
19606  * @extends Roo.bootstrap.Component
19607  * Bootstrap Popover class
19608  * @cfg {String} html contents of the popover   (or false to use children..)
19609  * @cfg {String} title of popover (or false to hide)
19610  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19611  * @cfg {String} trigger click || hover (or false to trigger manually)
19612  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19613  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19614  *      - if false and it has a 'parent' then it will be automatically added to that element
19615  *      - if string - Roo.get  will be called 
19616  * @cfg {Number} delay - delay before showing
19617  
19618  * @constructor
19619  * Create a new Popover
19620  * @param {Object} config The config object
19621  */
19622
19623 Roo.bootstrap.Popover = function(config){
19624     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19625     
19626     this.addEvents({
19627         // raw events
19628          /**
19629          * @event show
19630          * After the popover show
19631          * 
19632          * @param {Roo.bootstrap.Popover} this
19633          */
19634         "show" : true,
19635         /**
19636          * @event hide
19637          * After the popover hide
19638          * 
19639          * @param {Roo.bootstrap.Popover} this
19640          */
19641         "hide" : true
19642     });
19643 };
19644
19645 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19646     
19647     title: false,
19648     html: false,
19649     
19650     placement : 'right',
19651     trigger : 'hover', // hover
19652     modal : false,
19653     delay : 0,
19654     
19655     over: false,
19656     
19657     can_build_overlaid : false,
19658     
19659     maskEl : false, // the mask element
19660     headerEl : false,
19661     contentEl : false,
19662     alignEl : false, // when show is called with an element - this get's stored.
19663     
19664     getChildContainer : function()
19665     {
19666         return this.contentEl;
19667         
19668     },
19669     getPopoverHeader : function()
19670     {
19671         this.title = true; // flag not to hide it..
19672         this.headerEl.addClass('p-0');
19673         return this.headerEl
19674     },
19675     
19676     
19677     getAutoCreate : function(){
19678          
19679         var cfg = {
19680            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19681            style: 'display:block',
19682            cn : [
19683                 {
19684                     cls : 'arrow'
19685                 },
19686                 {
19687                     cls : 'popover-inner ',
19688                     cn : [
19689                         {
19690                             tag: 'h3',
19691                             cls: 'popover-title popover-header',
19692                             html : this.title === false ? '' : this.title
19693                         },
19694                         {
19695                             cls : 'popover-content popover-body '  + (this.cls || ''),
19696                             html : this.html || ''
19697                         }
19698                     ]
19699                     
19700                 }
19701            ]
19702         };
19703         
19704         return cfg;
19705     },
19706     /**
19707      * @param {string} the title
19708      */
19709     setTitle: function(str)
19710     {
19711         this.title = str;
19712         if (this.el) {
19713             this.headerEl.dom.innerHTML = str;
19714         }
19715         
19716     },
19717     /**
19718      * @param {string} the body content
19719      */
19720     setContent: function(str)
19721     {
19722         this.html = str;
19723         if (this.contentEl) {
19724             this.contentEl.dom.innerHTML = str;
19725         }
19726         
19727     },
19728     // as it get's added to the bottom of the page.
19729     onRender : function(ct, position)
19730     {
19731         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19732         
19733         
19734         
19735         if(!this.el){
19736             var cfg = Roo.apply({},  this.getAutoCreate());
19737             cfg.id = Roo.id();
19738             
19739             if (this.cls) {
19740                 cfg.cls += ' ' + this.cls;
19741             }
19742             if (this.style) {
19743                 cfg.style = this.style;
19744             }
19745             //Roo.log("adding to ");
19746             this.el = Roo.get(document.body).createChild(cfg, position);
19747 //            Roo.log(this.el);
19748         }
19749         
19750         this.contentEl = this.el.select('.popover-content',true).first();
19751         this.headerEl =  this.el.select('.popover-title',true).first();
19752         
19753         var nitems = [];
19754         if(typeof(this.items) != 'undefined'){
19755             var items = this.items;
19756             delete this.items;
19757
19758             for(var i =0;i < items.length;i++) {
19759                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19760             }
19761         }
19762
19763         this.items = nitems;
19764         
19765         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19766         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19767         
19768         
19769         
19770         this.initEvents();
19771     },
19772     
19773     resizeMask : function()
19774     {
19775         this.maskEl.setSize(
19776             Roo.lib.Dom.getViewWidth(true),
19777             Roo.lib.Dom.getViewHeight(true)
19778         );
19779     },
19780     
19781     initEvents : function()
19782     {
19783         
19784         if (!this.modal) { 
19785             Roo.bootstrap.Popover.register(this);
19786         }
19787          
19788         this.arrowEl = this.el.select('.arrow',true).first();
19789         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19790         this.el.enableDisplayMode('block');
19791         this.el.hide();
19792  
19793         
19794         if (this.over === false && !this.parent()) {
19795             return; 
19796         }
19797         if (this.triggers === false) {
19798             return;
19799         }
19800          
19801         // support parent
19802         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19803         var triggers = this.trigger ? this.trigger.split(' ') : [];
19804         Roo.each(triggers, function(trigger) {
19805         
19806             if (trigger == 'click') {
19807                 on_el.on('click', this.toggle, this);
19808             } else if (trigger != 'manual') {
19809                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19810                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19811       
19812                 on_el.on(eventIn  ,this.enter, this);
19813                 on_el.on(eventOut, this.leave, this);
19814             }
19815         }, this);
19816     },
19817     
19818     
19819     // private
19820     timeout : null,
19821     hoverState : null,
19822     
19823     toggle : function () {
19824         this.hoverState == 'in' ? this.leave() : this.enter();
19825     },
19826     
19827     enter : function () {
19828         
19829         clearTimeout(this.timeout);
19830     
19831         this.hoverState = 'in';
19832     
19833         if (!this.delay || !this.delay.show) {
19834             this.show();
19835             return;
19836         }
19837         var _t = this;
19838         this.timeout = setTimeout(function () {
19839             if (_t.hoverState == 'in') {
19840                 _t.show();
19841             }
19842         }, this.delay.show)
19843     },
19844     
19845     leave : function() {
19846         clearTimeout(this.timeout);
19847     
19848         this.hoverState = 'out';
19849     
19850         if (!this.delay || !this.delay.hide) {
19851             this.hide();
19852             return;
19853         }
19854         var _t = this;
19855         this.timeout = setTimeout(function () {
19856             if (_t.hoverState == 'out') {
19857                 _t.hide();
19858             }
19859         }, this.delay.hide)
19860     },
19861     /**
19862      * Show the popover
19863      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19864      * @param {string} (left|right|top|bottom) position
19865      */
19866     show : function (on_el, placement)
19867     {
19868         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19869         on_el = on_el || false; // default to false
19870          
19871         if (!on_el) {
19872             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19873                 on_el = this.parent().el;
19874             } else if (this.over) {
19875                 Roo.get(this.over);
19876             }
19877             
19878         }
19879         
19880         if (!this.el) {
19881             this.render(document.body);
19882         }
19883         
19884         
19885         this.el.removeClass([
19886             'fade','top','bottom', 'left', 'right','in',
19887             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19888         ]);
19889         
19890         if (this.title === false) {
19891             this.headerEl.hide();
19892         }
19893         
19894        
19895         this.el.show();
19896         this.el.dom.style.display = 'block';
19897          
19898         
19899         this.el.addClass(placement + ' roo-popover-' + placement);
19900
19901         if (on_el) {
19902             this.updatePosition();
19903              
19904         } else {
19905             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19906             var es = this.el.getSize();
19907             var x = Roo.lib.Dom.getViewWidth()/2;
19908             var y = Roo.lib.Dom.getViewHeight()/2;
19909             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19910             
19911         }
19912
19913         
19914         //var arrow = this.el.select('.arrow',true).first();
19915         //arrow.set(align[2], 
19916         
19917         this.el.addClass('in');
19918         
19919          
19920         
19921         this.hoverState = 'in';
19922         
19923         if (this.modal) {
19924             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19925             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19926             this.maskEl.dom.style.display = 'block';
19927             this.maskEl.addClass('show');
19928         }
19929         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19930  
19931         this.fireEvent('show', this);
19932         
19933     },
19934     /**
19935      * fire this manually after loading a grid in the table for example
19936      * @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 (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19952                     //normal display... or moved up/down.
19953                     this.setXY(offset);
19954                     var xy = this.alignEl.getAnchorXY('tr', false);
19955                     xy[0]+=2;xy[1]+=5;
19956                     this.arrowEl.setXY(xy);
19957                     return true;
19958                 }
19959                 return this.updatePosition('left', false);
19960             
19961             case 'left':
19962                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19963                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[-10,0]);
19964                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19965                     //normal display... or moved up/down.
19966                     this.setXY(offset);
19967                     var xy = this.alignEl.getAnchorXY('tl', false);
19968                     xy[0]+=2;xy[1]+=5;
19969                     this.arrowEl.setXY(xy);
19970                     return true;
19971                 }
19972                 return this.updatePosition('right', false);
19973             
19974             
19975         }
19976         
19977         
19978         
19979         // work out the pointy position.
19980         var p1 = this.alignment[0].split('-').pop().replace('?','');
19981         var xy = this.alignEl.getAnchorXY(p1, false);
19982         xy[0]+=2;xy[1]+=5;
19983         this.arrowEl.setXY(xy);
19984         return true;
19985     },
19986     
19987     hide : function()
19988     {
19989         this.el.setXY([0,0]);
19990         this.el.removeClass('in');
19991         this.el.hide();
19992         this.hoverState = null;
19993         this.maskEl.hide(); // always..
19994         this.fireEvent('hide', this);
19995     }
19996     
19997 });
19998
19999
20000 Roo.apply(Roo.bootstrap.Popover, {
20001
20002     alignment : {
20003         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20004         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20005         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20006         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20007     },
20008     
20009     zIndex : 20001,
20010
20011     clickHander : false,
20012     
20013
20014     onMouseDown : function(e)
20015     {
20016         if (!e.getTarget(".roo-popover")) {
20017             this.hideAll();
20018         }
20019          
20020     },
20021     
20022     popups : [],
20023     
20024     register : function(popup)
20025     {
20026         if (!Roo.bootstrap.Popover.clickHandler) {
20027             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20028         }
20029         // hide other popups.
20030         this.hideAll();
20031         this.popups.push(popup);
20032     },
20033     hideAll : function()
20034     {
20035         this.popups.forEach(function(p) {
20036             p.hide();
20037         });
20038     }
20039
20040 });/*
20041  * - LGPL
20042  *
20043  * Card header - holder for the card header elements.
20044  * 
20045  */
20046
20047 /**
20048  * @class Roo.bootstrap.PopoverNav
20049  * @extends Roo.bootstrap.NavGroup
20050  * Bootstrap Popover header navigation class
20051  * @constructor
20052  * Create a new Popover Header Navigation 
20053  * @param {Object} config The config object
20054  */
20055
20056 Roo.bootstrap.PopoverNav = function(config){
20057     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20058 };
20059
20060 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20061     
20062     
20063     container_method : 'getPopoverHeader' 
20064     
20065      
20066     
20067     
20068    
20069 });
20070
20071  
20072
20073  /*
20074  * - LGPL
20075  *
20076  * Progress
20077  * 
20078  */
20079
20080 /**
20081  * @class Roo.bootstrap.Progress
20082  * @extends Roo.bootstrap.Component
20083  * Bootstrap Progress class
20084  * @cfg {Boolean} striped striped of the progress bar
20085  * @cfg {Boolean} active animated of the progress bar
20086  * 
20087  * 
20088  * @constructor
20089  * Create a new Progress
20090  * @param {Object} config The config object
20091  */
20092
20093 Roo.bootstrap.Progress = function(config){
20094     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20095 };
20096
20097 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20098     
20099     striped : false,
20100     active: false,
20101     
20102     getAutoCreate : function(){
20103         var cfg = {
20104             tag: 'div',
20105             cls: 'progress'
20106         };
20107         
20108         
20109         if(this.striped){
20110             cfg.cls += ' progress-striped';
20111         }
20112       
20113         if(this.active){
20114             cfg.cls += ' active';
20115         }
20116         
20117         
20118         return cfg;
20119     }
20120    
20121 });
20122
20123  
20124
20125  /*
20126  * - LGPL
20127  *
20128  * ProgressBar
20129  * 
20130  */
20131
20132 /**
20133  * @class Roo.bootstrap.ProgressBar
20134  * @extends Roo.bootstrap.Component
20135  * Bootstrap ProgressBar class
20136  * @cfg {Number} aria_valuenow aria-value now
20137  * @cfg {Number} aria_valuemin aria-value min
20138  * @cfg {Number} aria_valuemax aria-value max
20139  * @cfg {String} label label for the progress bar
20140  * @cfg {String} panel (success | info | warning | danger )
20141  * @cfg {String} role role of the progress bar
20142  * @cfg {String} sr_only text
20143  * 
20144  * 
20145  * @constructor
20146  * Create a new ProgressBar
20147  * @param {Object} config The config object
20148  */
20149
20150 Roo.bootstrap.ProgressBar = function(config){
20151     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20152 };
20153
20154 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20155     
20156     aria_valuenow : 0,
20157     aria_valuemin : 0,
20158     aria_valuemax : 100,
20159     label : false,
20160     panel : false,
20161     role : false,
20162     sr_only: false,
20163     
20164     getAutoCreate : function()
20165     {
20166         
20167         var cfg = {
20168             tag: 'div',
20169             cls: 'progress-bar',
20170             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20171         };
20172         
20173         if(this.sr_only){
20174             cfg.cn = {
20175                 tag: 'span',
20176                 cls: 'sr-only',
20177                 html: this.sr_only
20178             }
20179         }
20180         
20181         if(this.role){
20182             cfg.role = this.role;
20183         }
20184         
20185         if(this.aria_valuenow){
20186             cfg['aria-valuenow'] = this.aria_valuenow;
20187         }
20188         
20189         if(this.aria_valuemin){
20190             cfg['aria-valuemin'] = this.aria_valuemin;
20191         }
20192         
20193         if(this.aria_valuemax){
20194             cfg['aria-valuemax'] = this.aria_valuemax;
20195         }
20196         
20197         if(this.label && !this.sr_only){
20198             cfg.html = this.label;
20199         }
20200         
20201         if(this.panel){
20202             cfg.cls += ' progress-bar-' + this.panel;
20203         }
20204         
20205         return cfg;
20206     },
20207     
20208     update : function(aria_valuenow)
20209     {
20210         this.aria_valuenow = aria_valuenow;
20211         
20212         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20213     }
20214    
20215 });
20216
20217  
20218
20219  /*
20220  * - LGPL
20221  *
20222  * column
20223  * 
20224  */
20225
20226 /**
20227  * @class Roo.bootstrap.TabGroup
20228  * @extends Roo.bootstrap.Column
20229  * Bootstrap Column class
20230  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20231  * @cfg {Boolean} carousel true to make the group behave like a carousel
20232  * @cfg {Boolean} bullets show bullets for the panels
20233  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20234  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20235  * @cfg {Boolean} showarrow (true|false) show arrow default true
20236  * 
20237  * @constructor
20238  * Create a new TabGroup
20239  * @param {Object} config The config object
20240  */
20241
20242 Roo.bootstrap.TabGroup = function(config){
20243     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20244     if (!this.navId) {
20245         this.navId = Roo.id();
20246     }
20247     this.tabs = [];
20248     Roo.bootstrap.TabGroup.register(this);
20249     
20250 };
20251
20252 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20253     
20254     carousel : false,
20255     transition : false,
20256     bullets : 0,
20257     timer : 0,
20258     autoslide : false,
20259     slideFn : false,
20260     slideOnTouch : false,
20261     showarrow : true,
20262     
20263     getAutoCreate : function()
20264     {
20265         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20266         
20267         cfg.cls += ' tab-content';
20268         
20269         if (this.carousel) {
20270             cfg.cls += ' carousel slide';
20271             
20272             cfg.cn = [{
20273                cls : 'carousel-inner',
20274                cn : []
20275             }];
20276         
20277             if(this.bullets  && !Roo.isTouch){
20278                 
20279                 var bullets = {
20280                     cls : 'carousel-bullets',
20281                     cn : []
20282                 };
20283                
20284                 if(this.bullets_cls){
20285                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20286                 }
20287                 
20288                 bullets.cn.push({
20289                     cls : 'clear'
20290                 });
20291                 
20292                 cfg.cn[0].cn.push(bullets);
20293             }
20294             
20295             if(this.showarrow){
20296                 cfg.cn[0].cn.push({
20297                     tag : 'div',
20298                     class : 'carousel-arrow',
20299                     cn : [
20300                         {
20301                             tag : 'div',
20302                             class : 'carousel-prev',
20303                             cn : [
20304                                 {
20305                                     tag : 'i',
20306                                     class : 'fa fa-chevron-left'
20307                                 }
20308                             ]
20309                         },
20310                         {
20311                             tag : 'div',
20312                             class : 'carousel-next',
20313                             cn : [
20314                                 {
20315                                     tag : 'i',
20316                                     class : 'fa fa-chevron-right'
20317                                 }
20318                             ]
20319                         }
20320                     ]
20321                 });
20322             }
20323             
20324         }
20325         
20326         return cfg;
20327     },
20328     
20329     initEvents:  function()
20330     {
20331 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20332 //            this.el.on("touchstart", this.onTouchStart, this);
20333 //        }
20334         
20335         if(this.autoslide){
20336             var _this = this;
20337             
20338             this.slideFn = window.setInterval(function() {
20339                 _this.showPanelNext();
20340             }, this.timer);
20341         }
20342         
20343         if(this.showarrow){
20344             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20345             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20346         }
20347         
20348         
20349     },
20350     
20351 //    onTouchStart : function(e, el, o)
20352 //    {
20353 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20354 //            return;
20355 //        }
20356 //        
20357 //        this.showPanelNext();
20358 //    },
20359     
20360     
20361     getChildContainer : function()
20362     {
20363         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20364     },
20365     
20366     /**
20367     * register a Navigation item
20368     * @param {Roo.bootstrap.NavItem} the navitem to add
20369     */
20370     register : function(item)
20371     {
20372         this.tabs.push( item);
20373         item.navId = this.navId; // not really needed..
20374         this.addBullet();
20375     
20376     },
20377     
20378     getActivePanel : function()
20379     {
20380         var r = false;
20381         Roo.each(this.tabs, function(t) {
20382             if (t.active) {
20383                 r = t;
20384                 return false;
20385             }
20386             return null;
20387         });
20388         return r;
20389         
20390     },
20391     getPanelByName : function(n)
20392     {
20393         var r = false;
20394         Roo.each(this.tabs, function(t) {
20395             if (t.tabId == n) {
20396                 r = t;
20397                 return false;
20398             }
20399             return null;
20400         });
20401         return r;
20402     },
20403     indexOfPanel : function(p)
20404     {
20405         var r = false;
20406         Roo.each(this.tabs, function(t,i) {
20407             if (t.tabId == p.tabId) {
20408                 r = i;
20409                 return false;
20410             }
20411             return null;
20412         });
20413         return r;
20414     },
20415     /**
20416      * show a specific panel
20417      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20418      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20419      */
20420     showPanel : function (pan)
20421     {
20422         if(this.transition || typeof(pan) == 'undefined'){
20423             Roo.log("waiting for the transitionend");
20424             return false;
20425         }
20426         
20427         if (typeof(pan) == 'number') {
20428             pan = this.tabs[pan];
20429         }
20430         
20431         if (typeof(pan) == 'string') {
20432             pan = this.getPanelByName(pan);
20433         }
20434         
20435         var cur = this.getActivePanel();
20436         
20437         if(!pan || !cur){
20438             Roo.log('pan or acitve pan is undefined');
20439             return false;
20440         }
20441         
20442         if (pan.tabId == this.getActivePanel().tabId) {
20443             return true;
20444         }
20445         
20446         if (false === cur.fireEvent('beforedeactivate')) {
20447             return false;
20448         }
20449         
20450         if(this.bullets > 0 && !Roo.isTouch){
20451             this.setActiveBullet(this.indexOfPanel(pan));
20452         }
20453         
20454         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20455             
20456             //class="carousel-item carousel-item-next carousel-item-left"
20457             
20458             this.transition = true;
20459             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20460             var lr = dir == 'next' ? 'left' : 'right';
20461             pan.el.addClass(dir); // or prev
20462             pan.el.addClass('carousel-item-' + dir); // or prev
20463             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20464             cur.el.addClass(lr); // or right
20465             pan.el.addClass(lr);
20466             cur.el.addClass('carousel-item-' +lr); // or right
20467             pan.el.addClass('carousel-item-' +lr);
20468             
20469             
20470             var _this = this;
20471             cur.el.on('transitionend', function() {
20472                 Roo.log("trans end?");
20473                 
20474                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20475                 pan.setActive(true);
20476                 
20477                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20478                 cur.setActive(false);
20479                 
20480                 _this.transition = false;
20481                 
20482             }, this, { single:  true } );
20483             
20484             return true;
20485         }
20486         
20487         cur.setActive(false);
20488         pan.setActive(true);
20489         
20490         return true;
20491         
20492     },
20493     showPanelNext : function()
20494     {
20495         var i = this.indexOfPanel(this.getActivePanel());
20496         
20497         if (i >= this.tabs.length - 1 && !this.autoslide) {
20498             return;
20499         }
20500         
20501         if (i >= this.tabs.length - 1 && this.autoslide) {
20502             i = -1;
20503         }
20504         
20505         this.showPanel(this.tabs[i+1]);
20506     },
20507     
20508     showPanelPrev : function()
20509     {
20510         var i = this.indexOfPanel(this.getActivePanel());
20511         
20512         if (i  < 1 && !this.autoslide) {
20513             return;
20514         }
20515         
20516         if (i < 1 && this.autoslide) {
20517             i = this.tabs.length;
20518         }
20519         
20520         this.showPanel(this.tabs[i-1]);
20521     },
20522     
20523     
20524     addBullet: function()
20525     {
20526         if(!this.bullets || Roo.isTouch){
20527             return;
20528         }
20529         var ctr = this.el.select('.carousel-bullets',true).first();
20530         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20531         var bullet = ctr.createChild({
20532             cls : 'bullet bullet-' + i
20533         },ctr.dom.lastChild);
20534         
20535         
20536         var _this = this;
20537         
20538         bullet.on('click', (function(e, el, o, ii, t){
20539
20540             e.preventDefault();
20541
20542             this.showPanel(ii);
20543
20544             if(this.autoslide && this.slideFn){
20545                 clearInterval(this.slideFn);
20546                 this.slideFn = window.setInterval(function() {
20547                     _this.showPanelNext();
20548                 }, this.timer);
20549             }
20550
20551         }).createDelegate(this, [i, bullet], true));
20552                 
20553         
20554     },
20555      
20556     setActiveBullet : function(i)
20557     {
20558         if(Roo.isTouch){
20559             return;
20560         }
20561         
20562         Roo.each(this.el.select('.bullet', true).elements, function(el){
20563             el.removeClass('selected');
20564         });
20565
20566         var bullet = this.el.select('.bullet-' + i, true).first();
20567         
20568         if(!bullet){
20569             return;
20570         }
20571         
20572         bullet.addClass('selected');
20573     }
20574     
20575     
20576   
20577 });
20578
20579  
20580
20581  
20582  
20583 Roo.apply(Roo.bootstrap.TabGroup, {
20584     
20585     groups: {},
20586      /**
20587     * register a Navigation Group
20588     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20589     */
20590     register : function(navgrp)
20591     {
20592         this.groups[navgrp.navId] = navgrp;
20593         
20594     },
20595     /**
20596     * fetch a Navigation Group based on the navigation ID
20597     * if one does not exist , it will get created.
20598     * @param {string} the navgroup to add
20599     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20600     */
20601     get: function(navId) {
20602         if (typeof(this.groups[navId]) == 'undefined') {
20603             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20604         }
20605         return this.groups[navId] ;
20606     }
20607     
20608     
20609     
20610 });
20611
20612  /*
20613  * - LGPL
20614  *
20615  * TabPanel
20616  * 
20617  */
20618
20619 /**
20620  * @class Roo.bootstrap.TabPanel
20621  * @extends Roo.bootstrap.Component
20622  * Bootstrap TabPanel class
20623  * @cfg {Boolean} active panel active
20624  * @cfg {String} html panel content
20625  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20626  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20627  * @cfg {String} href click to link..
20628  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20629  * 
20630  * 
20631  * @constructor
20632  * Create a new TabPanel
20633  * @param {Object} config The config object
20634  */
20635
20636 Roo.bootstrap.TabPanel = function(config){
20637     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20638     this.addEvents({
20639         /**
20640              * @event changed
20641              * Fires when the active status changes
20642              * @param {Roo.bootstrap.TabPanel} this
20643              * @param {Boolean} state the new state
20644             
20645          */
20646         'changed': true,
20647         /**
20648              * @event beforedeactivate
20649              * Fires before a tab is de-activated - can be used to do validation on a form.
20650              * @param {Roo.bootstrap.TabPanel} this
20651              * @return {Boolean} false if there is an error
20652             
20653          */
20654         'beforedeactivate': true
20655      });
20656     
20657     this.tabId = this.tabId || Roo.id();
20658   
20659 };
20660
20661 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20662     
20663     active: false,
20664     html: false,
20665     tabId: false,
20666     navId : false,
20667     href : '',
20668     touchSlide : false,
20669     getAutoCreate : function(){
20670         
20671         
20672         var cfg = {
20673             tag: 'div',
20674             // item is needed for carousel - not sure if it has any effect otherwise
20675             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20676             html: this.html || ''
20677         };
20678         
20679         if(this.active){
20680             cfg.cls += ' active';
20681         }
20682         
20683         if(this.tabId){
20684             cfg.tabId = this.tabId;
20685         }
20686         
20687         
20688         
20689         return cfg;
20690     },
20691     
20692     initEvents:  function()
20693     {
20694         var p = this.parent();
20695         
20696         this.navId = this.navId || p.navId;
20697         
20698         if (typeof(this.navId) != 'undefined') {
20699             // not really needed.. but just in case.. parent should be a NavGroup.
20700             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20701             
20702             tg.register(this);
20703             
20704             var i = tg.tabs.length - 1;
20705             
20706             if(this.active && tg.bullets > 0 && i < tg.bullets){
20707                 tg.setActiveBullet(i);
20708             }
20709         }
20710         
20711         this.el.on('click', this.onClick, this);
20712         
20713         if(Roo.isTouch && this.touchSlide){
20714             this.el.on("touchstart", this.onTouchStart, this);
20715             this.el.on("touchmove", this.onTouchMove, this);
20716             this.el.on("touchend", this.onTouchEnd, this);
20717         }
20718         
20719     },
20720     
20721     onRender : function(ct, position)
20722     {
20723         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20724     },
20725     
20726     setActive : function(state)
20727     {
20728         Roo.log("panel - set active " + this.tabId + "=" + state);
20729         
20730         this.active = state;
20731         if (!state) {
20732             this.el.removeClass('active');
20733             
20734         } else  if (!this.el.hasClass('active')) {
20735             this.el.addClass('active');
20736         }
20737         
20738         this.fireEvent('changed', this, state);
20739     },
20740     
20741     onClick : function(e)
20742     {
20743         e.preventDefault();
20744         
20745         if(!this.href.length){
20746             return;
20747         }
20748         
20749         window.location.href = this.href;
20750     },
20751     
20752     startX : 0,
20753     startY : 0,
20754     endX : 0,
20755     endY : 0,
20756     swiping : false,
20757     
20758     onTouchStart : function(e)
20759     {
20760         this.swiping = false;
20761         
20762         this.startX = e.browserEvent.touches[0].clientX;
20763         this.startY = e.browserEvent.touches[0].clientY;
20764     },
20765     
20766     onTouchMove : function(e)
20767     {
20768         this.swiping = true;
20769         
20770         this.endX = e.browserEvent.touches[0].clientX;
20771         this.endY = e.browserEvent.touches[0].clientY;
20772     },
20773     
20774     onTouchEnd : function(e)
20775     {
20776         if(!this.swiping){
20777             this.onClick(e);
20778             return;
20779         }
20780         
20781         var tabGroup = this.parent();
20782         
20783         if(this.endX > this.startX){ // swiping right
20784             tabGroup.showPanelPrev();
20785             return;
20786         }
20787         
20788         if(this.startX > this.endX){ // swiping left
20789             tabGroup.showPanelNext();
20790             return;
20791         }
20792     }
20793     
20794     
20795 });
20796  
20797
20798  
20799
20800  /*
20801  * - LGPL
20802  *
20803  * DateField
20804  * 
20805  */
20806
20807 /**
20808  * @class Roo.bootstrap.DateField
20809  * @extends Roo.bootstrap.Input
20810  * Bootstrap DateField class
20811  * @cfg {Number} weekStart default 0
20812  * @cfg {String} viewMode default empty, (months|years)
20813  * @cfg {String} minViewMode default empty, (months|years)
20814  * @cfg {Number} startDate default -Infinity
20815  * @cfg {Number} endDate default Infinity
20816  * @cfg {Boolean} todayHighlight default false
20817  * @cfg {Boolean} todayBtn default false
20818  * @cfg {Boolean} calendarWeeks default false
20819  * @cfg {Object} daysOfWeekDisabled default empty
20820  * @cfg {Boolean} singleMode default false (true | false)
20821  * 
20822  * @cfg {Boolean} keyboardNavigation default true
20823  * @cfg {String} language default en
20824  * 
20825  * @constructor
20826  * Create a new DateField
20827  * @param {Object} config The config object
20828  */
20829
20830 Roo.bootstrap.DateField = function(config){
20831     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20832      this.addEvents({
20833             /**
20834              * @event show
20835              * Fires when this field show.
20836              * @param {Roo.bootstrap.DateField} this
20837              * @param {Mixed} date The date value
20838              */
20839             show : true,
20840             /**
20841              * @event show
20842              * Fires when this field hide.
20843              * @param {Roo.bootstrap.DateField} this
20844              * @param {Mixed} date The date value
20845              */
20846             hide : true,
20847             /**
20848              * @event select
20849              * Fires when select a date.
20850              * @param {Roo.bootstrap.DateField} this
20851              * @param {Mixed} date The date value
20852              */
20853             select : true,
20854             /**
20855              * @event beforeselect
20856              * Fires when before select a date.
20857              * @param {Roo.bootstrap.DateField} this
20858              * @param {Mixed} date The date value
20859              */
20860             beforeselect : true
20861         });
20862 };
20863
20864 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20865     
20866     /**
20867      * @cfg {String} format
20868      * The default date format string which can be overriden for localization support.  The format must be
20869      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20870      */
20871     format : "m/d/y",
20872     /**
20873      * @cfg {String} altFormats
20874      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20875      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20876      */
20877     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20878     
20879     weekStart : 0,
20880     
20881     viewMode : '',
20882     
20883     minViewMode : '',
20884     
20885     todayHighlight : false,
20886     
20887     todayBtn: false,
20888     
20889     language: 'en',
20890     
20891     keyboardNavigation: true,
20892     
20893     calendarWeeks: false,
20894     
20895     startDate: -Infinity,
20896     
20897     endDate: Infinity,
20898     
20899     daysOfWeekDisabled: [],
20900     
20901     _events: [],
20902     
20903     singleMode : false,
20904     
20905     UTCDate: function()
20906     {
20907         return new Date(Date.UTC.apply(Date, arguments));
20908     },
20909     
20910     UTCToday: function()
20911     {
20912         var today = new Date();
20913         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20914     },
20915     
20916     getDate: function() {
20917             var d = this.getUTCDate();
20918             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20919     },
20920     
20921     getUTCDate: function() {
20922             return this.date;
20923     },
20924     
20925     setDate: function(d) {
20926             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20927     },
20928     
20929     setUTCDate: function(d) {
20930             this.date = d;
20931             this.setValue(this.formatDate(this.date));
20932     },
20933         
20934     onRender: function(ct, position)
20935     {
20936         
20937         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20938         
20939         this.language = this.language || 'en';
20940         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20941         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20942         
20943         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20944         this.format = this.format || 'm/d/y';
20945         this.isInline = false;
20946         this.isInput = true;
20947         this.component = this.el.select('.add-on', true).first() || false;
20948         this.component = (this.component && this.component.length === 0) ? false : this.component;
20949         this.hasInput = this.component && this.inputEl().length;
20950         
20951         if (typeof(this.minViewMode === 'string')) {
20952             switch (this.minViewMode) {
20953                 case 'months':
20954                     this.minViewMode = 1;
20955                     break;
20956                 case 'years':
20957                     this.minViewMode = 2;
20958                     break;
20959                 default:
20960                     this.minViewMode = 0;
20961                     break;
20962             }
20963         }
20964         
20965         if (typeof(this.viewMode === 'string')) {
20966             switch (this.viewMode) {
20967                 case 'months':
20968                     this.viewMode = 1;
20969                     break;
20970                 case 'years':
20971                     this.viewMode = 2;
20972                     break;
20973                 default:
20974                     this.viewMode = 0;
20975                     break;
20976             }
20977         }
20978                 
20979         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20980         
20981 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20982         
20983         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20984         
20985         this.picker().on('mousedown', this.onMousedown, this);
20986         this.picker().on('click', this.onClick, this);
20987         
20988         this.picker().addClass('datepicker-dropdown');
20989         
20990         this.startViewMode = this.viewMode;
20991         
20992         if(this.singleMode){
20993             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20994                 v.setVisibilityMode(Roo.Element.DISPLAY);
20995                 v.hide();
20996             });
20997             
20998             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20999                 v.setStyle('width', '189px');
21000             });
21001         }
21002         
21003         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21004             if(!this.calendarWeeks){
21005                 v.remove();
21006                 return;
21007             }
21008             
21009             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21010             v.attr('colspan', function(i, val){
21011                 return parseInt(val) + 1;
21012             });
21013         });
21014                         
21015         
21016         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21017         
21018         this.setStartDate(this.startDate);
21019         this.setEndDate(this.endDate);
21020         
21021         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21022         
21023         this.fillDow();
21024         this.fillMonths();
21025         this.update();
21026         this.showMode();
21027         
21028         if(this.isInline) {
21029             this.showPopup();
21030         }
21031     },
21032     
21033     picker : function()
21034     {
21035         return this.pickerEl;
21036 //        return this.el.select('.datepicker', true).first();
21037     },
21038     
21039     fillDow: function()
21040     {
21041         var dowCnt = this.weekStart;
21042         
21043         var dow = {
21044             tag: 'tr',
21045             cn: [
21046                 
21047             ]
21048         };
21049         
21050         if(this.calendarWeeks){
21051             dow.cn.push({
21052                 tag: 'th',
21053                 cls: 'cw',
21054                 html: '&nbsp;'
21055             })
21056         }
21057         
21058         while (dowCnt < this.weekStart + 7) {
21059             dow.cn.push({
21060                 tag: 'th',
21061                 cls: 'dow',
21062                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21063             });
21064         }
21065         
21066         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21067     },
21068     
21069     fillMonths: function()
21070     {    
21071         var i = 0;
21072         var months = this.picker().select('>.datepicker-months td', true).first();
21073         
21074         months.dom.innerHTML = '';
21075         
21076         while (i < 12) {
21077             var month = {
21078                 tag: 'span',
21079                 cls: 'month',
21080                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21081             };
21082             
21083             months.createChild(month);
21084         }
21085         
21086     },
21087     
21088     update: function()
21089     {
21090         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;
21091         
21092         if (this.date < this.startDate) {
21093             this.viewDate = new Date(this.startDate);
21094         } else if (this.date > this.endDate) {
21095             this.viewDate = new Date(this.endDate);
21096         } else {
21097             this.viewDate = new Date(this.date);
21098         }
21099         
21100         this.fill();
21101     },
21102     
21103     fill: function() 
21104     {
21105         var d = new Date(this.viewDate),
21106                 year = d.getUTCFullYear(),
21107                 month = d.getUTCMonth(),
21108                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21109                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21110                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21111                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21112                 currentDate = this.date && this.date.valueOf(),
21113                 today = this.UTCToday();
21114         
21115         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21116         
21117 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21118         
21119 //        this.picker.select('>tfoot th.today').
21120 //                                              .text(dates[this.language].today)
21121 //                                              .toggle(this.todayBtn !== false);
21122     
21123         this.updateNavArrows();
21124         this.fillMonths();
21125                                                 
21126         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21127         
21128         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21129          
21130         prevMonth.setUTCDate(day);
21131         
21132         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21133         
21134         var nextMonth = new Date(prevMonth);
21135         
21136         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21137         
21138         nextMonth = nextMonth.valueOf();
21139         
21140         var fillMonths = false;
21141         
21142         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21143         
21144         while(prevMonth.valueOf() <= nextMonth) {
21145             var clsName = '';
21146             
21147             if (prevMonth.getUTCDay() === this.weekStart) {
21148                 if(fillMonths){
21149                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21150                 }
21151                     
21152                 fillMonths = {
21153                     tag: 'tr',
21154                     cn: []
21155                 };
21156                 
21157                 if(this.calendarWeeks){
21158                     // ISO 8601: First week contains first thursday.
21159                     // ISO also states week starts on Monday, but we can be more abstract here.
21160                     var
21161                     // Start of current week: based on weekstart/current date
21162                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21163                     // Thursday of this week
21164                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21165                     // First Thursday of year, year from thursday
21166                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21167                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21168                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21169                     
21170                     fillMonths.cn.push({
21171                         tag: 'td',
21172                         cls: 'cw',
21173                         html: calWeek
21174                     });
21175                 }
21176             }
21177             
21178             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21179                 clsName += ' old';
21180             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21181                 clsName += ' new';
21182             }
21183             if (this.todayHighlight &&
21184                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21185                 prevMonth.getUTCMonth() == today.getMonth() &&
21186                 prevMonth.getUTCDate() == today.getDate()) {
21187                 clsName += ' today';
21188             }
21189             
21190             if (currentDate && prevMonth.valueOf() === currentDate) {
21191                 clsName += ' active';
21192             }
21193             
21194             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21195                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21196                     clsName += ' disabled';
21197             }
21198             
21199             fillMonths.cn.push({
21200                 tag: 'td',
21201                 cls: 'day ' + clsName,
21202                 html: prevMonth.getDate()
21203             });
21204             
21205             prevMonth.setDate(prevMonth.getDate()+1);
21206         }
21207           
21208         var currentYear = this.date && this.date.getUTCFullYear();
21209         var currentMonth = this.date && this.date.getUTCMonth();
21210         
21211         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21212         
21213         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21214             v.removeClass('active');
21215             
21216             if(currentYear === year && k === currentMonth){
21217                 v.addClass('active');
21218             }
21219             
21220             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21221                 v.addClass('disabled');
21222             }
21223             
21224         });
21225         
21226         
21227         year = parseInt(year/10, 10) * 10;
21228         
21229         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21230         
21231         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21232         
21233         year -= 1;
21234         for (var i = -1; i < 11; i++) {
21235             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21236                 tag: 'span',
21237                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21238                 html: year
21239             });
21240             
21241             year += 1;
21242         }
21243     },
21244     
21245     showMode: function(dir) 
21246     {
21247         if (dir) {
21248             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21249         }
21250         
21251         Roo.each(this.picker().select('>div',true).elements, function(v){
21252             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21253             v.hide();
21254         });
21255         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21256     },
21257     
21258     place: function()
21259     {
21260         if(this.isInline) {
21261             return;
21262         }
21263         
21264         this.picker().removeClass(['bottom', 'top']);
21265         
21266         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21267             /*
21268              * place to the top of element!
21269              *
21270              */
21271             
21272             this.picker().addClass('top');
21273             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21274             
21275             return;
21276         }
21277         
21278         this.picker().addClass('bottom');
21279         
21280         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21281     },
21282     
21283     parseDate : function(value)
21284     {
21285         if(!value || value instanceof Date){
21286             return value;
21287         }
21288         var v = Date.parseDate(value, this.format);
21289         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21290             v = Date.parseDate(value, 'Y-m-d');
21291         }
21292         if(!v && this.altFormats){
21293             if(!this.altFormatsArray){
21294                 this.altFormatsArray = this.altFormats.split("|");
21295             }
21296             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21297                 v = Date.parseDate(value, this.altFormatsArray[i]);
21298             }
21299         }
21300         return v;
21301     },
21302     
21303     formatDate : function(date, fmt)
21304     {   
21305         return (!date || !(date instanceof Date)) ?
21306         date : date.dateFormat(fmt || this.format);
21307     },
21308     
21309     onFocus : function()
21310     {
21311         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21312         this.showPopup();
21313     },
21314     
21315     onBlur : function()
21316     {
21317         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21318         
21319         var d = this.inputEl().getValue();
21320         
21321         this.setValue(d);
21322                 
21323         this.hidePopup();
21324     },
21325     
21326     showPopup : function()
21327     {
21328         this.picker().show();
21329         this.update();
21330         this.place();
21331         
21332         this.fireEvent('showpopup', this, this.date);
21333     },
21334     
21335     hidePopup : function()
21336     {
21337         if(this.isInline) {
21338             return;
21339         }
21340         this.picker().hide();
21341         this.viewMode = this.startViewMode;
21342         this.showMode();
21343         
21344         this.fireEvent('hidepopup', this, this.date);
21345         
21346     },
21347     
21348     onMousedown: function(e)
21349     {
21350         e.stopPropagation();
21351         e.preventDefault();
21352     },
21353     
21354     keyup: function(e)
21355     {
21356         Roo.bootstrap.DateField.superclass.keyup.call(this);
21357         this.update();
21358     },
21359
21360     setValue: function(v)
21361     {
21362         if(this.fireEvent('beforeselect', this, v) !== false){
21363             var d = new Date(this.parseDate(v) ).clearTime();
21364         
21365             if(isNaN(d.getTime())){
21366                 this.date = this.viewDate = '';
21367                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21368                 return;
21369             }
21370
21371             v = this.formatDate(d);
21372
21373             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21374
21375             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21376
21377             this.update();
21378
21379             this.fireEvent('select', this, this.date);
21380         }
21381     },
21382     
21383     getValue: function()
21384     {
21385         return this.formatDate(this.date);
21386     },
21387     
21388     fireKey: function(e)
21389     {
21390         if (!this.picker().isVisible()){
21391             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21392                 this.showPopup();
21393             }
21394             return;
21395         }
21396         
21397         var dateChanged = false,
21398         dir, day, month,
21399         newDate, newViewDate;
21400         
21401         switch(e.keyCode){
21402             case 27: // escape
21403                 this.hidePopup();
21404                 e.preventDefault();
21405                 break;
21406             case 37: // left
21407             case 39: // right
21408                 if (!this.keyboardNavigation) {
21409                     break;
21410                 }
21411                 dir = e.keyCode == 37 ? -1 : 1;
21412                 
21413                 if (e.ctrlKey){
21414                     newDate = this.moveYear(this.date, dir);
21415                     newViewDate = this.moveYear(this.viewDate, dir);
21416                 } else if (e.shiftKey){
21417                     newDate = this.moveMonth(this.date, dir);
21418                     newViewDate = this.moveMonth(this.viewDate, dir);
21419                 } else {
21420                     newDate = new Date(this.date);
21421                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21422                     newViewDate = new Date(this.viewDate);
21423                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21424                 }
21425                 if (this.dateWithinRange(newDate)){
21426                     this.date = newDate;
21427                     this.viewDate = newViewDate;
21428                     this.setValue(this.formatDate(this.date));
21429 //                    this.update();
21430                     e.preventDefault();
21431                     dateChanged = true;
21432                 }
21433                 break;
21434             case 38: // up
21435             case 40: // down
21436                 if (!this.keyboardNavigation) {
21437                     break;
21438                 }
21439                 dir = e.keyCode == 38 ? -1 : 1;
21440                 if (e.ctrlKey){
21441                     newDate = this.moveYear(this.date, dir);
21442                     newViewDate = this.moveYear(this.viewDate, dir);
21443                 } else if (e.shiftKey){
21444                     newDate = this.moveMonth(this.date, dir);
21445                     newViewDate = this.moveMonth(this.viewDate, dir);
21446                 } else {
21447                     newDate = new Date(this.date);
21448                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21449                     newViewDate = new Date(this.viewDate);
21450                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21451                 }
21452                 if (this.dateWithinRange(newDate)){
21453                     this.date = newDate;
21454                     this.viewDate = newViewDate;
21455                     this.setValue(this.formatDate(this.date));
21456 //                    this.update();
21457                     e.preventDefault();
21458                     dateChanged = true;
21459                 }
21460                 break;
21461             case 13: // enter
21462                 this.setValue(this.formatDate(this.date));
21463                 this.hidePopup();
21464                 e.preventDefault();
21465                 break;
21466             case 9: // tab
21467                 this.setValue(this.formatDate(this.date));
21468                 this.hidePopup();
21469                 break;
21470             case 16: // shift
21471             case 17: // ctrl
21472             case 18: // alt
21473                 break;
21474             default :
21475                 this.hidePopup();
21476                 
21477         }
21478     },
21479     
21480     
21481     onClick: function(e) 
21482     {
21483         e.stopPropagation();
21484         e.preventDefault();
21485         
21486         var target = e.getTarget();
21487         
21488         if(target.nodeName.toLowerCase() === 'i'){
21489             target = Roo.get(target).dom.parentNode;
21490         }
21491         
21492         var nodeName = target.nodeName;
21493         var className = target.className;
21494         var html = target.innerHTML;
21495         //Roo.log(nodeName);
21496         
21497         switch(nodeName.toLowerCase()) {
21498             case 'th':
21499                 switch(className) {
21500                     case 'switch':
21501                         this.showMode(1);
21502                         break;
21503                     case 'prev':
21504                     case 'next':
21505                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21506                         switch(this.viewMode){
21507                                 case 0:
21508                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21509                                         break;
21510                                 case 1:
21511                                 case 2:
21512                                         this.viewDate = this.moveYear(this.viewDate, dir);
21513                                         break;
21514                         }
21515                         this.fill();
21516                         break;
21517                     case 'today':
21518                         var date = new Date();
21519                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21520 //                        this.fill()
21521                         this.setValue(this.formatDate(this.date));
21522                         
21523                         this.hidePopup();
21524                         break;
21525                 }
21526                 break;
21527             case 'span':
21528                 if (className.indexOf('disabled') < 0) {
21529                     this.viewDate.setUTCDate(1);
21530                     if (className.indexOf('month') > -1) {
21531                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21532                     } else {
21533                         var year = parseInt(html, 10) || 0;
21534                         this.viewDate.setUTCFullYear(year);
21535                         
21536                     }
21537                     
21538                     if(this.singleMode){
21539                         this.setValue(this.formatDate(this.viewDate));
21540                         this.hidePopup();
21541                         return;
21542                     }
21543                     
21544                     this.showMode(-1);
21545                     this.fill();
21546                 }
21547                 break;
21548                 
21549             case 'td':
21550                 //Roo.log(className);
21551                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21552                     var day = parseInt(html, 10) || 1;
21553                     var year = this.viewDate.getUTCFullYear(),
21554                         month = this.viewDate.getUTCMonth();
21555
21556                     if (className.indexOf('old') > -1) {
21557                         if(month === 0 ){
21558                             month = 11;
21559                             year -= 1;
21560                         }else{
21561                             month -= 1;
21562                         }
21563                     } else if (className.indexOf('new') > -1) {
21564                         if (month == 11) {
21565                             month = 0;
21566                             year += 1;
21567                         } else {
21568                             month += 1;
21569                         }
21570                     }
21571                     //Roo.log([year,month,day]);
21572                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21573                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21574 //                    this.fill();
21575                     //Roo.log(this.formatDate(this.date));
21576                     this.setValue(this.formatDate(this.date));
21577                     this.hidePopup();
21578                 }
21579                 break;
21580         }
21581     },
21582     
21583     setStartDate: function(startDate)
21584     {
21585         this.startDate = startDate || -Infinity;
21586         if (this.startDate !== -Infinity) {
21587             this.startDate = this.parseDate(this.startDate);
21588         }
21589         this.update();
21590         this.updateNavArrows();
21591     },
21592
21593     setEndDate: function(endDate)
21594     {
21595         this.endDate = endDate || Infinity;
21596         if (this.endDate !== Infinity) {
21597             this.endDate = this.parseDate(this.endDate);
21598         }
21599         this.update();
21600         this.updateNavArrows();
21601     },
21602     
21603     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21604     {
21605         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21606         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21607             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21608         }
21609         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21610             return parseInt(d, 10);
21611         });
21612         this.update();
21613         this.updateNavArrows();
21614     },
21615     
21616     updateNavArrows: function() 
21617     {
21618         if(this.singleMode){
21619             return;
21620         }
21621         
21622         var d = new Date(this.viewDate),
21623         year = d.getUTCFullYear(),
21624         month = d.getUTCMonth();
21625         
21626         Roo.each(this.picker().select('.prev', true).elements, function(v){
21627             v.show();
21628             switch (this.viewMode) {
21629                 case 0:
21630
21631                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21632                         v.hide();
21633                     }
21634                     break;
21635                 case 1:
21636                 case 2:
21637                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21638                         v.hide();
21639                     }
21640                     break;
21641             }
21642         });
21643         
21644         Roo.each(this.picker().select('.next', true).elements, function(v){
21645             v.show();
21646             switch (this.viewMode) {
21647                 case 0:
21648
21649                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21650                         v.hide();
21651                     }
21652                     break;
21653                 case 1:
21654                 case 2:
21655                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21656                         v.hide();
21657                     }
21658                     break;
21659             }
21660         })
21661     },
21662     
21663     moveMonth: function(date, dir)
21664     {
21665         if (!dir) {
21666             return date;
21667         }
21668         var new_date = new Date(date.valueOf()),
21669         day = new_date.getUTCDate(),
21670         month = new_date.getUTCMonth(),
21671         mag = Math.abs(dir),
21672         new_month, test;
21673         dir = dir > 0 ? 1 : -1;
21674         if (mag == 1){
21675             test = dir == -1
21676             // If going back one month, make sure month is not current month
21677             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21678             ? function(){
21679                 return new_date.getUTCMonth() == month;
21680             }
21681             // If going forward one month, make sure month is as expected
21682             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21683             : function(){
21684                 return new_date.getUTCMonth() != new_month;
21685             };
21686             new_month = month + dir;
21687             new_date.setUTCMonth(new_month);
21688             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21689             if (new_month < 0 || new_month > 11) {
21690                 new_month = (new_month + 12) % 12;
21691             }
21692         } else {
21693             // For magnitudes >1, move one month at a time...
21694             for (var i=0; i<mag; i++) {
21695                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21696                 new_date = this.moveMonth(new_date, dir);
21697             }
21698             // ...then reset the day, keeping it in the new month
21699             new_month = new_date.getUTCMonth();
21700             new_date.setUTCDate(day);
21701             test = function(){
21702                 return new_month != new_date.getUTCMonth();
21703             };
21704         }
21705         // Common date-resetting loop -- if date is beyond end of month, make it
21706         // end of month
21707         while (test()){
21708             new_date.setUTCDate(--day);
21709             new_date.setUTCMonth(new_month);
21710         }
21711         return new_date;
21712     },
21713
21714     moveYear: function(date, dir)
21715     {
21716         return this.moveMonth(date, dir*12);
21717     },
21718
21719     dateWithinRange: function(date)
21720     {
21721         return date >= this.startDate && date <= this.endDate;
21722     },
21723
21724     
21725     remove: function() 
21726     {
21727         this.picker().remove();
21728     },
21729     
21730     validateValue : function(value)
21731     {
21732         if(this.getVisibilityEl().hasClass('hidden')){
21733             return true;
21734         }
21735         
21736         if(value.length < 1)  {
21737             if(this.allowBlank){
21738                 return true;
21739             }
21740             return false;
21741         }
21742         
21743         if(value.length < this.minLength){
21744             return false;
21745         }
21746         if(value.length > this.maxLength){
21747             return false;
21748         }
21749         if(this.vtype){
21750             var vt = Roo.form.VTypes;
21751             if(!vt[this.vtype](value, this)){
21752                 return false;
21753             }
21754         }
21755         if(typeof this.validator == "function"){
21756             var msg = this.validator(value);
21757             if(msg !== true){
21758                 return false;
21759             }
21760         }
21761         
21762         if(this.regex && !this.regex.test(value)){
21763             return false;
21764         }
21765         
21766         if(typeof(this.parseDate(value)) == 'undefined'){
21767             return false;
21768         }
21769         
21770         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21771             return false;
21772         }      
21773         
21774         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21775             return false;
21776         } 
21777         
21778         
21779         return true;
21780     },
21781     
21782     reset : function()
21783     {
21784         this.date = this.viewDate = '';
21785         
21786         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21787     }
21788    
21789 });
21790
21791 Roo.apply(Roo.bootstrap.DateField,  {
21792     
21793     head : {
21794         tag: 'thead',
21795         cn: [
21796         {
21797             tag: 'tr',
21798             cn: [
21799             {
21800                 tag: 'th',
21801                 cls: 'prev',
21802                 html: '<i class="fa fa-arrow-left"/>'
21803             },
21804             {
21805                 tag: 'th',
21806                 cls: 'switch',
21807                 colspan: '5'
21808             },
21809             {
21810                 tag: 'th',
21811                 cls: 'next',
21812                 html: '<i class="fa fa-arrow-right"/>'
21813             }
21814
21815             ]
21816         }
21817         ]
21818     },
21819     
21820     content : {
21821         tag: 'tbody',
21822         cn: [
21823         {
21824             tag: 'tr',
21825             cn: [
21826             {
21827                 tag: 'td',
21828                 colspan: '7'
21829             }
21830             ]
21831         }
21832         ]
21833     },
21834     
21835     footer : {
21836         tag: 'tfoot',
21837         cn: [
21838         {
21839             tag: 'tr',
21840             cn: [
21841             {
21842                 tag: 'th',
21843                 colspan: '7',
21844                 cls: 'today'
21845             }
21846                     
21847             ]
21848         }
21849         ]
21850     },
21851     
21852     dates:{
21853         en: {
21854             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21855             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21856             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21857             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21858             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21859             today: "Today"
21860         }
21861     },
21862     
21863     modes: [
21864     {
21865         clsName: 'days',
21866         navFnc: 'Month',
21867         navStep: 1
21868     },
21869     {
21870         clsName: 'months',
21871         navFnc: 'FullYear',
21872         navStep: 1
21873     },
21874     {
21875         clsName: 'years',
21876         navFnc: 'FullYear',
21877         navStep: 10
21878     }]
21879 });
21880
21881 Roo.apply(Roo.bootstrap.DateField,  {
21882   
21883     template : {
21884         tag: 'div',
21885         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21886         cn: [
21887         {
21888             tag: 'div',
21889             cls: 'datepicker-days',
21890             cn: [
21891             {
21892                 tag: 'table',
21893                 cls: 'table-condensed',
21894                 cn:[
21895                 Roo.bootstrap.DateField.head,
21896                 {
21897                     tag: 'tbody'
21898                 },
21899                 Roo.bootstrap.DateField.footer
21900                 ]
21901             }
21902             ]
21903         },
21904         {
21905             tag: 'div',
21906             cls: 'datepicker-months',
21907             cn: [
21908             {
21909                 tag: 'table',
21910                 cls: 'table-condensed',
21911                 cn:[
21912                 Roo.bootstrap.DateField.head,
21913                 Roo.bootstrap.DateField.content,
21914                 Roo.bootstrap.DateField.footer
21915                 ]
21916             }
21917             ]
21918         },
21919         {
21920             tag: 'div',
21921             cls: 'datepicker-years',
21922             cn: [
21923             {
21924                 tag: 'table',
21925                 cls: 'table-condensed',
21926                 cn:[
21927                 Roo.bootstrap.DateField.head,
21928                 Roo.bootstrap.DateField.content,
21929                 Roo.bootstrap.DateField.footer
21930                 ]
21931             }
21932             ]
21933         }
21934         ]
21935     }
21936 });
21937
21938  
21939
21940  /*
21941  * - LGPL
21942  *
21943  * TimeField
21944  * 
21945  */
21946
21947 /**
21948  * @class Roo.bootstrap.TimeField
21949  * @extends Roo.bootstrap.Input
21950  * Bootstrap DateField class
21951  * 
21952  * 
21953  * @constructor
21954  * Create a new TimeField
21955  * @param {Object} config The config object
21956  */
21957
21958 Roo.bootstrap.TimeField = function(config){
21959     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21960     this.addEvents({
21961             /**
21962              * @event show
21963              * Fires when this field show.
21964              * @param {Roo.bootstrap.DateField} thisthis
21965              * @param {Mixed} date The date value
21966              */
21967             show : true,
21968             /**
21969              * @event show
21970              * Fires when this field hide.
21971              * @param {Roo.bootstrap.DateField} this
21972              * @param {Mixed} date The date value
21973              */
21974             hide : true,
21975             /**
21976              * @event select
21977              * Fires when select a date.
21978              * @param {Roo.bootstrap.DateField} this
21979              * @param {Mixed} date The date value
21980              */
21981             select : true
21982         });
21983 };
21984
21985 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21986     
21987     /**
21988      * @cfg {String} format
21989      * The default time format string which can be overriden for localization support.  The format must be
21990      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21991      */
21992     format : "H:i",
21993
21994     getAutoCreate : function()
21995     {
21996         this.after = '<i class="fa far fa-clock"></i>';
21997         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
21998         
21999          
22000     },
22001     onRender: function(ct, position)
22002     {
22003         
22004         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22005                 
22006         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22007         
22008         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22009         
22010         this.pop = this.picker().select('>.datepicker-time',true).first();
22011         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22012         
22013         this.picker().on('mousedown', this.onMousedown, this);
22014         this.picker().on('click', this.onClick, this);
22015         
22016         this.picker().addClass('datepicker-dropdown');
22017     
22018         this.fillTime();
22019         this.update();
22020             
22021         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22022         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22023         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22024         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22025         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22026         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22027
22028     },
22029     
22030     fireKey: function(e){
22031         if (!this.picker().isVisible()){
22032             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22033                 this.show();
22034             }
22035             return;
22036         }
22037
22038         e.preventDefault();
22039         
22040         switch(e.keyCode){
22041             case 27: // escape
22042                 this.hide();
22043                 break;
22044             case 37: // left
22045             case 39: // right
22046                 this.onTogglePeriod();
22047                 break;
22048             case 38: // up
22049                 this.onIncrementMinutes();
22050                 break;
22051             case 40: // down
22052                 this.onDecrementMinutes();
22053                 break;
22054             case 13: // enter
22055             case 9: // tab
22056                 this.setTime();
22057                 break;
22058         }
22059     },
22060     
22061     onClick: function(e) {
22062         e.stopPropagation();
22063         e.preventDefault();
22064     },
22065     
22066     picker : function()
22067     {
22068         return this.pickerEl;
22069     },
22070     
22071     fillTime: function()
22072     {    
22073         var time = this.pop.select('tbody', true).first();
22074         
22075         time.dom.innerHTML = '';
22076         
22077         time.createChild({
22078             tag: 'tr',
22079             cn: [
22080                 {
22081                     tag: 'td',
22082                     cn: [
22083                         {
22084                             tag: 'a',
22085                             href: '#',
22086                             cls: 'btn',
22087                             cn: [
22088                                 {
22089                                     tag: 'i',
22090                                     cls: 'hours-up fa fas fa-chevron-up'
22091                                 }
22092                             ]
22093                         } 
22094                     ]
22095                 },
22096                 {
22097                     tag: 'td',
22098                     cls: 'separator'
22099                 },
22100                 {
22101                     tag: 'td',
22102                     cn: [
22103                         {
22104                             tag: 'a',
22105                             href: '#',
22106                             cls: 'btn',
22107                             cn: [
22108                                 {
22109                                     tag: 'i',
22110                                     cls: 'minutes-up fa fas fa-chevron-up'
22111                                 }
22112                             ]
22113                         }
22114                     ]
22115                 },
22116                 {
22117                     tag: 'td',
22118                     cls: 'separator'
22119                 }
22120             ]
22121         });
22122         
22123         time.createChild({
22124             tag: 'tr',
22125             cn: [
22126                 {
22127                     tag: 'td',
22128                     cn: [
22129                         {
22130                             tag: 'span',
22131                             cls: 'timepicker-hour',
22132                             html: '00'
22133                         }  
22134                     ]
22135                 },
22136                 {
22137                     tag: 'td',
22138                     cls: 'separator',
22139                     html: ':'
22140                 },
22141                 {
22142                     tag: 'td',
22143                     cn: [
22144                         {
22145                             tag: 'span',
22146                             cls: 'timepicker-minute',
22147                             html: '00'
22148                         }  
22149                     ]
22150                 },
22151                 {
22152                     tag: 'td',
22153                     cls: 'separator'
22154                 },
22155                 {
22156                     tag: 'td',
22157                     cn: [
22158                         {
22159                             tag: 'button',
22160                             type: 'button',
22161                             cls: 'btn btn-primary period',
22162                             html: 'AM'
22163                             
22164                         }
22165                     ]
22166                 }
22167             ]
22168         });
22169         
22170         time.createChild({
22171             tag: 'tr',
22172             cn: [
22173                 {
22174                     tag: 'td',
22175                     cn: [
22176                         {
22177                             tag: 'a',
22178                             href: '#',
22179                             cls: 'btn',
22180                             cn: [
22181                                 {
22182                                     tag: 'span',
22183                                     cls: 'hours-down fa fas fa-chevron-down'
22184                                 }
22185                             ]
22186                         }
22187                     ]
22188                 },
22189                 {
22190                     tag: 'td',
22191                     cls: 'separator'
22192                 },
22193                 {
22194                     tag: 'td',
22195                     cn: [
22196                         {
22197                             tag: 'a',
22198                             href: '#',
22199                             cls: 'btn',
22200                             cn: [
22201                                 {
22202                                     tag: 'span',
22203                                     cls: 'minutes-down fa fas fa-chevron-down'
22204                                 }
22205                             ]
22206                         }
22207                     ]
22208                 },
22209                 {
22210                     tag: 'td',
22211                     cls: 'separator'
22212                 }
22213             ]
22214         });
22215         
22216     },
22217     
22218     update: function()
22219     {
22220         
22221         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22222         
22223         this.fill();
22224     },
22225     
22226     fill: function() 
22227     {
22228         var hours = this.time.getHours();
22229         var minutes = this.time.getMinutes();
22230         var period = 'AM';
22231         
22232         if(hours > 11){
22233             period = 'PM';
22234         }
22235         
22236         if(hours == 0){
22237             hours = 12;
22238         }
22239         
22240         
22241         if(hours > 12){
22242             hours = hours - 12;
22243         }
22244         
22245         if(hours < 10){
22246             hours = '0' + hours;
22247         }
22248         
22249         if(minutes < 10){
22250             minutes = '0' + minutes;
22251         }
22252         
22253         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22254         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22255         this.pop.select('button', true).first().dom.innerHTML = period;
22256         
22257     },
22258     
22259     place: function()
22260     {   
22261         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22262         
22263         var cls = ['bottom'];
22264         
22265         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22266             cls.pop();
22267             cls.push('top');
22268         }
22269         
22270         cls.push('right');
22271         
22272         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22273             cls.pop();
22274             cls.push('left');
22275         }
22276         //this.picker().setXY(20000,20000);
22277         this.picker().addClass(cls.join('-'));
22278         
22279         var _this = this;
22280         
22281         Roo.each(cls, function(c){
22282             if(c == 'bottom'){
22283                 (function() {
22284                  //  
22285                 }).defer(200);
22286                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22287                 //_this.picker().setTop(_this.inputEl().getHeight());
22288                 return;
22289             }
22290             if(c == 'top'){
22291                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22292                 
22293                 //_this.picker().setTop(0 - _this.picker().getHeight());
22294                 return;
22295             }
22296             /*
22297             if(c == 'left'){
22298                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22299                 return;
22300             }
22301             if(c == 'right'){
22302                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22303                 return;
22304             }
22305             */
22306         });
22307         
22308     },
22309   
22310     onFocus : function()
22311     {
22312         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22313         this.show();
22314     },
22315     
22316     onBlur : function()
22317     {
22318         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22319         this.hide();
22320     },
22321     
22322     show : function()
22323     {
22324         this.picker().show();
22325         this.pop.show();
22326         this.update();
22327         this.place();
22328         
22329         this.fireEvent('show', this, this.date);
22330     },
22331     
22332     hide : function()
22333     {
22334         this.picker().hide();
22335         this.pop.hide();
22336         
22337         this.fireEvent('hide', this, this.date);
22338     },
22339     
22340     setTime : function()
22341     {
22342         this.hide();
22343         this.setValue(this.time.format(this.format));
22344         
22345         this.fireEvent('select', this, this.date);
22346         
22347         
22348     },
22349     
22350     onMousedown: function(e){
22351         e.stopPropagation();
22352         e.preventDefault();
22353     },
22354     
22355     onIncrementHours: function()
22356     {
22357         Roo.log('onIncrementHours');
22358         this.time = this.time.add(Date.HOUR, 1);
22359         this.update();
22360         
22361     },
22362     
22363     onDecrementHours: function()
22364     {
22365         Roo.log('onDecrementHours');
22366         this.time = this.time.add(Date.HOUR, -1);
22367         this.update();
22368     },
22369     
22370     onIncrementMinutes: function()
22371     {
22372         Roo.log('onIncrementMinutes');
22373         this.time = this.time.add(Date.MINUTE, 1);
22374         this.update();
22375     },
22376     
22377     onDecrementMinutes: function()
22378     {
22379         Roo.log('onDecrementMinutes');
22380         this.time = this.time.add(Date.MINUTE, -1);
22381         this.update();
22382     },
22383     
22384     onTogglePeriod: function()
22385     {
22386         Roo.log('onTogglePeriod');
22387         this.time = this.time.add(Date.HOUR, 12);
22388         this.update();
22389     }
22390     
22391    
22392 });
22393  
22394
22395 Roo.apply(Roo.bootstrap.TimeField,  {
22396   
22397     template : {
22398         tag: 'div',
22399         cls: 'datepicker dropdown-menu',
22400         cn: [
22401             {
22402                 tag: 'div',
22403                 cls: 'datepicker-time',
22404                 cn: [
22405                 {
22406                     tag: 'table',
22407                     cls: 'table-condensed',
22408                     cn:[
22409                         {
22410                             tag: 'tbody',
22411                             cn: [
22412                                 {
22413                                     tag: 'tr',
22414                                     cn: [
22415                                     {
22416                                         tag: 'td',
22417                                         colspan: '7'
22418                                     }
22419                                     ]
22420                                 }
22421                             ]
22422                         },
22423                         {
22424                             tag: 'tfoot',
22425                             cn: [
22426                                 {
22427                                     tag: 'tr',
22428                                     cn: [
22429                                     {
22430                                         tag: 'th',
22431                                         colspan: '7',
22432                                         cls: '',
22433                                         cn: [
22434                                             {
22435                                                 tag: 'button',
22436                                                 cls: 'btn btn-info ok',
22437                                                 html: 'OK'
22438                                             }
22439                                         ]
22440                                     }
22441                     
22442                                     ]
22443                                 }
22444                             ]
22445                         }
22446                     ]
22447                 }
22448                 ]
22449             }
22450         ]
22451     }
22452 });
22453
22454  
22455
22456  /*
22457  * - LGPL
22458  *
22459  * MonthField
22460  * 
22461  */
22462
22463 /**
22464  * @class Roo.bootstrap.MonthField
22465  * @extends Roo.bootstrap.Input
22466  * Bootstrap MonthField class
22467  * 
22468  * @cfg {String} language default en
22469  * 
22470  * @constructor
22471  * Create a new MonthField
22472  * @param {Object} config The config object
22473  */
22474
22475 Roo.bootstrap.MonthField = function(config){
22476     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22477     
22478     this.addEvents({
22479         /**
22480          * @event show
22481          * Fires when this field show.
22482          * @param {Roo.bootstrap.MonthField} this
22483          * @param {Mixed} date The date value
22484          */
22485         show : true,
22486         /**
22487          * @event show
22488          * Fires when this field hide.
22489          * @param {Roo.bootstrap.MonthField} this
22490          * @param {Mixed} date The date value
22491          */
22492         hide : true,
22493         /**
22494          * @event select
22495          * Fires when select a date.
22496          * @param {Roo.bootstrap.MonthField} this
22497          * @param {String} oldvalue The old value
22498          * @param {String} newvalue The new value
22499          */
22500         select : true
22501     });
22502 };
22503
22504 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22505     
22506     onRender: function(ct, position)
22507     {
22508         
22509         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22510         
22511         this.language = this.language || 'en';
22512         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22513         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22514         
22515         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22516         this.isInline = false;
22517         this.isInput = true;
22518         this.component = this.el.select('.add-on', true).first() || false;
22519         this.component = (this.component && this.component.length === 0) ? false : this.component;
22520         this.hasInput = this.component && this.inputEL().length;
22521         
22522         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22523         
22524         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22525         
22526         this.picker().on('mousedown', this.onMousedown, this);
22527         this.picker().on('click', this.onClick, this);
22528         
22529         this.picker().addClass('datepicker-dropdown');
22530         
22531         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22532             v.setStyle('width', '189px');
22533         });
22534         
22535         this.fillMonths();
22536         
22537         this.update();
22538         
22539         if(this.isInline) {
22540             this.show();
22541         }
22542         
22543     },
22544     
22545     setValue: function(v, suppressEvent)
22546     {   
22547         var o = this.getValue();
22548         
22549         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22550         
22551         this.update();
22552
22553         if(suppressEvent !== true){
22554             this.fireEvent('select', this, o, v);
22555         }
22556         
22557     },
22558     
22559     getValue: function()
22560     {
22561         return this.value;
22562     },
22563     
22564     onClick: function(e) 
22565     {
22566         e.stopPropagation();
22567         e.preventDefault();
22568         
22569         var target = e.getTarget();
22570         
22571         if(target.nodeName.toLowerCase() === 'i'){
22572             target = Roo.get(target).dom.parentNode;
22573         }
22574         
22575         var nodeName = target.nodeName;
22576         var className = target.className;
22577         var html = target.innerHTML;
22578         
22579         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22580             return;
22581         }
22582         
22583         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22584         
22585         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22586         
22587         this.hide();
22588                         
22589     },
22590     
22591     picker : function()
22592     {
22593         return this.pickerEl;
22594     },
22595     
22596     fillMonths: function()
22597     {    
22598         var i = 0;
22599         var months = this.picker().select('>.datepicker-months td', true).first();
22600         
22601         months.dom.innerHTML = '';
22602         
22603         while (i < 12) {
22604             var month = {
22605                 tag: 'span',
22606                 cls: 'month',
22607                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22608             };
22609             
22610             months.createChild(month);
22611         }
22612         
22613     },
22614     
22615     update: function()
22616     {
22617         var _this = this;
22618         
22619         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22620             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22621         }
22622         
22623         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22624             e.removeClass('active');
22625             
22626             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22627                 e.addClass('active');
22628             }
22629         })
22630     },
22631     
22632     place: function()
22633     {
22634         if(this.isInline) {
22635             return;
22636         }
22637         
22638         this.picker().removeClass(['bottom', 'top']);
22639         
22640         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22641             /*
22642              * place to the top of element!
22643              *
22644              */
22645             
22646             this.picker().addClass('top');
22647             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22648             
22649             return;
22650         }
22651         
22652         this.picker().addClass('bottom');
22653         
22654         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22655     },
22656     
22657     onFocus : function()
22658     {
22659         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22660         this.show();
22661     },
22662     
22663     onBlur : function()
22664     {
22665         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22666         
22667         var d = this.inputEl().getValue();
22668         
22669         this.setValue(d);
22670                 
22671         this.hide();
22672     },
22673     
22674     show : function()
22675     {
22676         this.picker().show();
22677         this.picker().select('>.datepicker-months', true).first().show();
22678         this.update();
22679         this.place();
22680         
22681         this.fireEvent('show', this, this.date);
22682     },
22683     
22684     hide : function()
22685     {
22686         if(this.isInline) {
22687             return;
22688         }
22689         this.picker().hide();
22690         this.fireEvent('hide', this, this.date);
22691         
22692     },
22693     
22694     onMousedown: function(e)
22695     {
22696         e.stopPropagation();
22697         e.preventDefault();
22698     },
22699     
22700     keyup: function(e)
22701     {
22702         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22703         this.update();
22704     },
22705
22706     fireKey: function(e)
22707     {
22708         if (!this.picker().isVisible()){
22709             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22710                 this.show();
22711             }
22712             return;
22713         }
22714         
22715         var dir;
22716         
22717         switch(e.keyCode){
22718             case 27: // escape
22719                 this.hide();
22720                 e.preventDefault();
22721                 break;
22722             case 37: // left
22723             case 39: // right
22724                 dir = e.keyCode == 37 ? -1 : 1;
22725                 
22726                 this.vIndex = this.vIndex + dir;
22727                 
22728                 if(this.vIndex < 0){
22729                     this.vIndex = 0;
22730                 }
22731                 
22732                 if(this.vIndex > 11){
22733                     this.vIndex = 11;
22734                 }
22735                 
22736                 if(isNaN(this.vIndex)){
22737                     this.vIndex = 0;
22738                 }
22739                 
22740                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22741                 
22742                 break;
22743             case 38: // up
22744             case 40: // down
22745                 
22746                 dir = e.keyCode == 38 ? -1 : 1;
22747                 
22748                 this.vIndex = this.vIndex + dir * 4;
22749                 
22750                 if(this.vIndex < 0){
22751                     this.vIndex = 0;
22752                 }
22753                 
22754                 if(this.vIndex > 11){
22755                     this.vIndex = 11;
22756                 }
22757                 
22758                 if(isNaN(this.vIndex)){
22759                     this.vIndex = 0;
22760                 }
22761                 
22762                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22763                 break;
22764                 
22765             case 13: // enter
22766                 
22767                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22768                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22769                 }
22770                 
22771                 this.hide();
22772                 e.preventDefault();
22773                 break;
22774             case 9: // tab
22775                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22776                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22777                 }
22778                 this.hide();
22779                 break;
22780             case 16: // shift
22781             case 17: // ctrl
22782             case 18: // alt
22783                 break;
22784             default :
22785                 this.hide();
22786                 
22787         }
22788     },
22789     
22790     remove: function() 
22791     {
22792         this.picker().remove();
22793     }
22794    
22795 });
22796
22797 Roo.apply(Roo.bootstrap.MonthField,  {
22798     
22799     content : {
22800         tag: 'tbody',
22801         cn: [
22802         {
22803             tag: 'tr',
22804             cn: [
22805             {
22806                 tag: 'td',
22807                 colspan: '7'
22808             }
22809             ]
22810         }
22811         ]
22812     },
22813     
22814     dates:{
22815         en: {
22816             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22817             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22818         }
22819     }
22820 });
22821
22822 Roo.apply(Roo.bootstrap.MonthField,  {
22823   
22824     template : {
22825         tag: 'div',
22826         cls: 'datepicker dropdown-menu roo-dynamic',
22827         cn: [
22828             {
22829                 tag: 'div',
22830                 cls: 'datepicker-months',
22831                 cn: [
22832                 {
22833                     tag: 'table',
22834                     cls: 'table-condensed',
22835                     cn:[
22836                         Roo.bootstrap.DateField.content
22837                     ]
22838                 }
22839                 ]
22840             }
22841         ]
22842     }
22843 });
22844
22845  
22846
22847  
22848  /*
22849  * - LGPL
22850  *
22851  * CheckBox
22852  * 
22853  */
22854
22855 /**
22856  * @class Roo.bootstrap.CheckBox
22857  * @extends Roo.bootstrap.Input
22858  * Bootstrap CheckBox class
22859  * 
22860  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22861  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22862  * @cfg {String} boxLabel The text that appears beside the checkbox
22863  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22864  * @cfg {Boolean} checked initnal the element
22865  * @cfg {Boolean} inline inline the element (default false)
22866  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22867  * @cfg {String} tooltip label tooltip
22868  * 
22869  * @constructor
22870  * Create a new CheckBox
22871  * @param {Object} config The config object
22872  */
22873
22874 Roo.bootstrap.CheckBox = function(config){
22875     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22876    
22877     this.addEvents({
22878         /**
22879         * @event check
22880         * Fires when the element is checked or unchecked.
22881         * @param {Roo.bootstrap.CheckBox} this This input
22882         * @param {Boolean} checked The new checked value
22883         */
22884        check : true,
22885        /**
22886         * @event click
22887         * Fires when the element is click.
22888         * @param {Roo.bootstrap.CheckBox} this This input
22889         */
22890        click : true
22891     });
22892     
22893 };
22894
22895 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22896   
22897     inputType: 'checkbox',
22898     inputValue: 1,
22899     valueOff: 0,
22900     boxLabel: false,
22901     checked: false,
22902     weight : false,
22903     inline: false,
22904     tooltip : '',
22905     
22906     // checkbox success does not make any sense really.. 
22907     invalidClass : "",
22908     validClass : "",
22909     
22910     
22911     getAutoCreate : function()
22912     {
22913         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22914         
22915         var id = Roo.id();
22916         
22917         var cfg = {};
22918         
22919         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22920         
22921         if(this.inline){
22922             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22923         }
22924         
22925         var input =  {
22926             tag: 'input',
22927             id : id,
22928             type : this.inputType,
22929             value : this.inputValue,
22930             cls : 'roo-' + this.inputType, //'form-box',
22931             placeholder : this.placeholder || ''
22932             
22933         };
22934         
22935         if(this.inputType != 'radio'){
22936             var hidden =  {
22937                 tag: 'input',
22938                 type : 'hidden',
22939                 cls : 'roo-hidden-value',
22940                 value : this.checked ? this.inputValue : this.valueOff
22941             };
22942         }
22943         
22944             
22945         if (this.weight) { // Validity check?
22946             cfg.cls += " " + this.inputType + "-" + this.weight;
22947         }
22948         
22949         if (this.disabled) {
22950             input.disabled=true;
22951         }
22952         
22953         if(this.checked){
22954             input.checked = this.checked;
22955         }
22956         
22957         if (this.name) {
22958             
22959             input.name = this.name;
22960             
22961             if(this.inputType != 'radio'){
22962                 hidden.name = this.name;
22963                 input.name = '_hidden_' + this.name;
22964             }
22965         }
22966         
22967         if (this.size) {
22968             input.cls += ' input-' + this.size;
22969         }
22970         
22971         var settings=this;
22972         
22973         ['xs','sm','md','lg'].map(function(size){
22974             if (settings[size]) {
22975                 cfg.cls += ' col-' + size + '-' + settings[size];
22976             }
22977         });
22978         
22979         var inputblock = input;
22980          
22981         if (this.before || this.after) {
22982             
22983             inputblock = {
22984                 cls : 'input-group',
22985                 cn :  [] 
22986             };
22987             
22988             if (this.before) {
22989                 inputblock.cn.push({
22990                     tag :'span',
22991                     cls : 'input-group-addon',
22992                     html : this.before
22993                 });
22994             }
22995             
22996             inputblock.cn.push(input);
22997             
22998             if(this.inputType != 'radio'){
22999                 inputblock.cn.push(hidden);
23000             }
23001             
23002             if (this.after) {
23003                 inputblock.cn.push({
23004                     tag :'span',
23005                     cls : 'input-group-addon',
23006                     html : this.after
23007                 });
23008             }
23009             
23010         }
23011         var boxLabelCfg = false;
23012         
23013         if(this.boxLabel){
23014            
23015             boxLabelCfg = {
23016                 tag: 'label',
23017                 //'for': id, // box label is handled by onclick - so no for...
23018                 cls: 'box-label',
23019                 html: this.boxLabel
23020             };
23021             if(this.tooltip){
23022                 boxLabelCfg.tooltip = this.tooltip;
23023             }
23024              
23025         }
23026         
23027         
23028         if (align ==='left' && this.fieldLabel.length) {
23029 //                Roo.log("left and has label");
23030             cfg.cn = [
23031                 {
23032                     tag: 'label',
23033                     'for' :  id,
23034                     cls : 'control-label',
23035                     html : this.fieldLabel
23036                 },
23037                 {
23038                     cls : "", 
23039                     cn: [
23040                         inputblock
23041                     ]
23042                 }
23043             ];
23044             
23045             if (boxLabelCfg) {
23046                 cfg.cn[1].cn.push(boxLabelCfg);
23047             }
23048             
23049             if(this.labelWidth > 12){
23050                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23051             }
23052             
23053             if(this.labelWidth < 13 && this.labelmd == 0){
23054                 this.labelmd = this.labelWidth;
23055             }
23056             
23057             if(this.labellg > 0){
23058                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23059                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23060             }
23061             
23062             if(this.labelmd > 0){
23063                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23064                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23065             }
23066             
23067             if(this.labelsm > 0){
23068                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23069                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23070             }
23071             
23072             if(this.labelxs > 0){
23073                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23074                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23075             }
23076             
23077         } else if ( this.fieldLabel.length) {
23078 //                Roo.log(" label");
23079                 cfg.cn = [
23080                    
23081                     {
23082                         tag: this.boxLabel ? 'span' : 'label',
23083                         'for': id,
23084                         cls: 'control-label box-input-label',
23085                         //cls : 'input-group-addon',
23086                         html : this.fieldLabel
23087                     },
23088                     
23089                     inputblock
23090                     
23091                 ];
23092                 if (boxLabelCfg) {
23093                     cfg.cn.push(boxLabelCfg);
23094                 }
23095
23096         } else {
23097             
23098 //                Roo.log(" no label && no align");
23099                 cfg.cn = [  inputblock ] ;
23100                 if (boxLabelCfg) {
23101                     cfg.cn.push(boxLabelCfg);
23102                 }
23103
23104                 
23105         }
23106         
23107        
23108         
23109         if(this.inputType != 'radio'){
23110             cfg.cn.push(hidden);
23111         }
23112         
23113         return cfg;
23114         
23115     },
23116     
23117     /**
23118      * return the real input element.
23119      */
23120     inputEl: function ()
23121     {
23122         return this.el.select('input.roo-' + this.inputType,true).first();
23123     },
23124     hiddenEl: function ()
23125     {
23126         return this.el.select('input.roo-hidden-value',true).first();
23127     },
23128     
23129     labelEl: function()
23130     {
23131         return this.el.select('label.control-label',true).first();
23132     },
23133     /* depricated... */
23134     
23135     label: function()
23136     {
23137         return this.labelEl();
23138     },
23139     
23140     boxLabelEl: function()
23141     {
23142         return this.el.select('label.box-label',true).first();
23143     },
23144     
23145     initEvents : function()
23146     {
23147 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23148         
23149         this.inputEl().on('click', this.onClick,  this);
23150         
23151         if (this.boxLabel) { 
23152             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23153         }
23154         
23155         this.startValue = this.getValue();
23156         
23157         if(this.groupId){
23158             Roo.bootstrap.CheckBox.register(this);
23159         }
23160     },
23161     
23162     onClick : function(e)
23163     {   
23164         if(this.fireEvent('click', this, e) !== false){
23165             this.setChecked(!this.checked);
23166         }
23167         
23168     },
23169     
23170     setChecked : function(state,suppressEvent)
23171     {
23172         this.startValue = this.getValue();
23173
23174         if(this.inputType == 'radio'){
23175             
23176             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23177                 e.dom.checked = false;
23178             });
23179             
23180             this.inputEl().dom.checked = true;
23181             
23182             this.inputEl().dom.value = this.inputValue;
23183             
23184             if(suppressEvent !== true){
23185                 this.fireEvent('check', this, true);
23186             }
23187             
23188             this.validate();
23189             
23190             return;
23191         }
23192         
23193         this.checked = state;
23194         
23195         this.inputEl().dom.checked = state;
23196         
23197         
23198         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23199         
23200         if(suppressEvent !== true){
23201             this.fireEvent('check', this, state);
23202         }
23203         
23204         this.validate();
23205     },
23206     
23207     getValue : function()
23208     {
23209         if(this.inputType == 'radio'){
23210             return this.getGroupValue();
23211         }
23212         
23213         return this.hiddenEl().dom.value;
23214         
23215     },
23216     
23217     getGroupValue : function()
23218     {
23219         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23220             return '';
23221         }
23222         
23223         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23224     },
23225     
23226     setValue : function(v,suppressEvent)
23227     {
23228         if(this.inputType == 'radio'){
23229             this.setGroupValue(v, suppressEvent);
23230             return;
23231         }
23232         
23233         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23234         
23235         this.validate();
23236     },
23237     
23238     setGroupValue : function(v, suppressEvent)
23239     {
23240         this.startValue = this.getValue();
23241         
23242         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23243             e.dom.checked = false;
23244             
23245             if(e.dom.value == v){
23246                 e.dom.checked = true;
23247             }
23248         });
23249         
23250         if(suppressEvent !== true){
23251             this.fireEvent('check', this, true);
23252         }
23253
23254         this.validate();
23255         
23256         return;
23257     },
23258     
23259     validate : function()
23260     {
23261         if(this.getVisibilityEl().hasClass('hidden')){
23262             return true;
23263         }
23264         
23265         if(
23266                 this.disabled || 
23267                 (this.inputType == 'radio' && this.validateRadio()) ||
23268                 (this.inputType == 'checkbox' && this.validateCheckbox())
23269         ){
23270             this.markValid();
23271             return true;
23272         }
23273         
23274         this.markInvalid();
23275         return false;
23276     },
23277     
23278     validateRadio : function()
23279     {
23280         if(this.getVisibilityEl().hasClass('hidden')){
23281             return true;
23282         }
23283         
23284         if(this.allowBlank){
23285             return true;
23286         }
23287         
23288         var valid = false;
23289         
23290         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23291             if(!e.dom.checked){
23292                 return;
23293             }
23294             
23295             valid = true;
23296             
23297             return false;
23298         });
23299         
23300         return valid;
23301     },
23302     
23303     validateCheckbox : function()
23304     {
23305         if(!this.groupId){
23306             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23307             //return (this.getValue() == this.inputValue) ? true : false;
23308         }
23309         
23310         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23311         
23312         if(!group){
23313             return false;
23314         }
23315         
23316         var r = false;
23317         
23318         for(var i in group){
23319             if(group[i].el.isVisible(true)){
23320                 r = false;
23321                 break;
23322             }
23323             
23324             r = true;
23325         }
23326         
23327         for(var i in group){
23328             if(r){
23329                 break;
23330             }
23331             
23332             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23333         }
23334         
23335         return r;
23336     },
23337     
23338     /**
23339      * Mark this field as valid
23340      */
23341     markValid : function()
23342     {
23343         var _this = this;
23344         
23345         this.fireEvent('valid', this);
23346         
23347         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23348         
23349         if(this.groupId){
23350             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23351         }
23352         
23353         if(label){
23354             label.markValid();
23355         }
23356
23357         if(this.inputType == 'radio'){
23358             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23359                 var fg = e.findParent('.form-group', false, true);
23360                 if (Roo.bootstrap.version == 3) {
23361                     fg.removeClass([_this.invalidClass, _this.validClass]);
23362                     fg.addClass(_this.validClass);
23363                 } else {
23364                     fg.removeClass(['is-valid', 'is-invalid']);
23365                     fg.addClass('is-valid');
23366                 }
23367             });
23368             
23369             return;
23370         }
23371
23372         if(!this.groupId){
23373             var fg = this.el.findParent('.form-group', false, true);
23374             if (Roo.bootstrap.version == 3) {
23375                 fg.removeClass([this.invalidClass, this.validClass]);
23376                 fg.addClass(this.validClass);
23377             } else {
23378                 fg.removeClass(['is-valid', 'is-invalid']);
23379                 fg.addClass('is-valid');
23380             }
23381             return;
23382         }
23383         
23384         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23385         
23386         if(!group){
23387             return;
23388         }
23389         
23390         for(var i in group){
23391             var fg = group[i].el.findParent('.form-group', false, true);
23392             if (Roo.bootstrap.version == 3) {
23393                 fg.removeClass([this.invalidClass, this.validClass]);
23394                 fg.addClass(this.validClass);
23395             } else {
23396                 fg.removeClass(['is-valid', 'is-invalid']);
23397                 fg.addClass('is-valid');
23398             }
23399         }
23400     },
23401     
23402      /**
23403      * Mark this field as invalid
23404      * @param {String} msg The validation message
23405      */
23406     markInvalid : function(msg)
23407     {
23408         if(this.allowBlank){
23409             return;
23410         }
23411         
23412         var _this = this;
23413         
23414         this.fireEvent('invalid', this, msg);
23415         
23416         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23417         
23418         if(this.groupId){
23419             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23420         }
23421         
23422         if(label){
23423             label.markInvalid();
23424         }
23425             
23426         if(this.inputType == 'radio'){
23427             
23428             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23429                 var fg = e.findParent('.form-group', false, true);
23430                 if (Roo.bootstrap.version == 3) {
23431                     fg.removeClass([_this.invalidClass, _this.validClass]);
23432                     fg.addClass(_this.invalidClass);
23433                 } else {
23434                     fg.removeClass(['is-invalid', 'is-valid']);
23435                     fg.addClass('is-invalid');
23436                 }
23437             });
23438             
23439             return;
23440         }
23441         
23442         if(!this.groupId){
23443             var fg = this.el.findParent('.form-group', false, true);
23444             if (Roo.bootstrap.version == 3) {
23445                 fg.removeClass([_this.invalidClass, _this.validClass]);
23446                 fg.addClass(_this.invalidClass);
23447             } else {
23448                 fg.removeClass(['is-invalid', 'is-valid']);
23449                 fg.addClass('is-invalid');
23450             }
23451             return;
23452         }
23453         
23454         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23455         
23456         if(!group){
23457             return;
23458         }
23459         
23460         for(var i in group){
23461             var fg = group[i].el.findParent('.form-group', false, true);
23462             if (Roo.bootstrap.version == 3) {
23463                 fg.removeClass([_this.invalidClass, _this.validClass]);
23464                 fg.addClass(_this.invalidClass);
23465             } else {
23466                 fg.removeClass(['is-invalid', 'is-valid']);
23467                 fg.addClass('is-invalid');
23468             }
23469         }
23470         
23471     },
23472     
23473     clearInvalid : function()
23474     {
23475         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23476         
23477         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23478         
23479         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23480         
23481         if (label && label.iconEl) {
23482             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23483             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23484         }
23485     },
23486     
23487     disable : function()
23488     {
23489         if(this.inputType != 'radio'){
23490             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23491             return;
23492         }
23493         
23494         var _this = this;
23495         
23496         if(this.rendered){
23497             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23498                 _this.getActionEl().addClass(this.disabledClass);
23499                 e.dom.disabled = true;
23500             });
23501         }
23502         
23503         this.disabled = true;
23504         this.fireEvent("disable", this);
23505         return this;
23506     },
23507
23508     enable : function()
23509     {
23510         if(this.inputType != 'radio'){
23511             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23512             return;
23513         }
23514         
23515         var _this = this;
23516         
23517         if(this.rendered){
23518             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23519                 _this.getActionEl().removeClass(this.disabledClass);
23520                 e.dom.disabled = false;
23521             });
23522         }
23523         
23524         this.disabled = false;
23525         this.fireEvent("enable", this);
23526         return this;
23527     },
23528     
23529     setBoxLabel : function(v)
23530     {
23531         this.boxLabel = v;
23532         
23533         if(this.rendered){
23534             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23535         }
23536     }
23537
23538 });
23539
23540 Roo.apply(Roo.bootstrap.CheckBox, {
23541     
23542     groups: {},
23543     
23544      /**
23545     * register a CheckBox Group
23546     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23547     */
23548     register : function(checkbox)
23549     {
23550         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23551             this.groups[checkbox.groupId] = {};
23552         }
23553         
23554         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23555             return;
23556         }
23557         
23558         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23559         
23560     },
23561     /**
23562     * fetch a CheckBox Group based on the group ID
23563     * @param {string} the group ID
23564     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23565     */
23566     get: function(groupId) {
23567         if (typeof(this.groups[groupId]) == 'undefined') {
23568             return false;
23569         }
23570         
23571         return this.groups[groupId] ;
23572     }
23573     
23574     
23575 });
23576 /*
23577  * - LGPL
23578  *
23579  * RadioItem
23580  * 
23581  */
23582
23583 /**
23584  * @class Roo.bootstrap.Radio
23585  * @extends Roo.bootstrap.Component
23586  * Bootstrap Radio class
23587  * @cfg {String} boxLabel - the label associated
23588  * @cfg {String} value - the value of radio
23589  * 
23590  * @constructor
23591  * Create a new Radio
23592  * @param {Object} config The config object
23593  */
23594 Roo.bootstrap.Radio = function(config){
23595     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23596     
23597 };
23598
23599 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23600     
23601     boxLabel : '',
23602     
23603     value : '',
23604     
23605     getAutoCreate : function()
23606     {
23607         var cfg = {
23608             tag : 'div',
23609             cls : 'form-group radio',
23610             cn : [
23611                 {
23612                     tag : 'label',
23613                     cls : 'box-label',
23614                     html : this.boxLabel
23615                 }
23616             ]
23617         };
23618         
23619         return cfg;
23620     },
23621     
23622     initEvents : function() 
23623     {
23624         this.parent().register(this);
23625         
23626         this.el.on('click', this.onClick, this);
23627         
23628     },
23629     
23630     onClick : function(e)
23631     {
23632         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23633             this.setChecked(true);
23634         }
23635     },
23636     
23637     setChecked : function(state, suppressEvent)
23638     {
23639         this.parent().setValue(this.value, suppressEvent);
23640         
23641     },
23642     
23643     setBoxLabel : function(v)
23644     {
23645         this.boxLabel = v;
23646         
23647         if(this.rendered){
23648             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23649         }
23650     }
23651     
23652 });
23653  
23654
23655  /*
23656  * - LGPL
23657  *
23658  * Input
23659  * 
23660  */
23661
23662 /**
23663  * @class Roo.bootstrap.SecurePass
23664  * @extends Roo.bootstrap.Input
23665  * Bootstrap SecurePass class
23666  *
23667  * 
23668  * @constructor
23669  * Create a new SecurePass
23670  * @param {Object} config The config object
23671  */
23672  
23673 Roo.bootstrap.SecurePass = function (config) {
23674     // these go here, so the translation tool can replace them..
23675     this.errors = {
23676         PwdEmpty: "Please type a password, and then retype it to confirm.",
23677         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23678         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23679         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23680         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23681         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23682         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23683         TooWeak: "Your password is Too Weak."
23684     },
23685     this.meterLabel = "Password strength:";
23686     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23687     this.meterClass = [
23688         "roo-password-meter-tooweak", 
23689         "roo-password-meter-weak", 
23690         "roo-password-meter-medium", 
23691         "roo-password-meter-strong", 
23692         "roo-password-meter-grey"
23693     ];
23694     
23695     this.errors = {};
23696     
23697     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23698 }
23699
23700 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23701     /**
23702      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23703      * {
23704      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23705      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23706      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23707      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23708      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23709      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23710      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23711      * })
23712      */
23713     // private
23714     
23715     meterWidth: 300,
23716     errorMsg :'',    
23717     errors: false,
23718     imageRoot: '/',
23719     /**
23720      * @cfg {String/Object} Label for the strength meter (defaults to
23721      * 'Password strength:')
23722      */
23723     // private
23724     meterLabel: '',
23725     /**
23726      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23727      * ['Weak', 'Medium', 'Strong'])
23728      */
23729     // private    
23730     pwdStrengths: false,    
23731     // private
23732     strength: 0,
23733     // private
23734     _lastPwd: null,
23735     // private
23736     kCapitalLetter: 0,
23737     kSmallLetter: 1,
23738     kDigit: 2,
23739     kPunctuation: 3,
23740     
23741     insecure: false,
23742     // private
23743     initEvents: function ()
23744     {
23745         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23746
23747         if (this.el.is('input[type=password]') && Roo.isSafari) {
23748             this.el.on('keydown', this.SafariOnKeyDown, this);
23749         }
23750
23751         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23752     },
23753     // private
23754     onRender: function (ct, position)
23755     {
23756         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23757         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23758         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23759
23760         this.trigger.createChild({
23761                    cn: [
23762                     {
23763                     //id: 'PwdMeter',
23764                     tag: 'div',
23765                     cls: 'roo-password-meter-grey col-xs-12',
23766                     style: {
23767                         //width: 0,
23768                         //width: this.meterWidth + 'px'                                                
23769                         }
23770                     },
23771                     {                            
23772                          cls: 'roo-password-meter-text'                          
23773                     }
23774                 ]            
23775         });
23776
23777          
23778         if (this.hideTrigger) {
23779             this.trigger.setDisplayed(false);
23780         }
23781         this.setSize(this.width || '', this.height || '');
23782     },
23783     // private
23784     onDestroy: function ()
23785     {
23786         if (this.trigger) {
23787             this.trigger.removeAllListeners();
23788             this.trigger.remove();
23789         }
23790         if (this.wrap) {
23791             this.wrap.remove();
23792         }
23793         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23794     },
23795     // private
23796     checkStrength: function ()
23797     {
23798         var pwd = this.inputEl().getValue();
23799         if (pwd == this._lastPwd) {
23800             return;
23801         }
23802
23803         var strength;
23804         if (this.ClientSideStrongPassword(pwd)) {
23805             strength = 3;
23806         } else if (this.ClientSideMediumPassword(pwd)) {
23807             strength = 2;
23808         } else if (this.ClientSideWeakPassword(pwd)) {
23809             strength = 1;
23810         } else {
23811             strength = 0;
23812         }
23813         
23814         Roo.log('strength1: ' + strength);
23815         
23816         //var pm = this.trigger.child('div/div/div').dom;
23817         var pm = this.trigger.child('div/div');
23818         pm.removeClass(this.meterClass);
23819         pm.addClass(this.meterClass[strength]);
23820                 
23821         
23822         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23823                 
23824         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23825         
23826         this._lastPwd = pwd;
23827     },
23828     reset: function ()
23829     {
23830         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23831         
23832         this._lastPwd = '';
23833         
23834         var pm = this.trigger.child('div/div');
23835         pm.removeClass(this.meterClass);
23836         pm.addClass('roo-password-meter-grey');        
23837         
23838         
23839         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23840         
23841         pt.innerHTML = '';
23842         this.inputEl().dom.type='password';
23843     },
23844     // private
23845     validateValue: function (value)
23846     {
23847         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23848             return false;
23849         }
23850         if (value.length == 0) {
23851             if (this.allowBlank) {
23852                 this.clearInvalid();
23853                 return true;
23854             }
23855
23856             this.markInvalid(this.errors.PwdEmpty);
23857             this.errorMsg = this.errors.PwdEmpty;
23858             return false;
23859         }
23860         
23861         if(this.insecure){
23862             return true;
23863         }
23864         
23865         if (!value.match(/[\x21-\x7e]+/)) {
23866             this.markInvalid(this.errors.PwdBadChar);
23867             this.errorMsg = this.errors.PwdBadChar;
23868             return false;
23869         }
23870         if (value.length < 6) {
23871             this.markInvalid(this.errors.PwdShort);
23872             this.errorMsg = this.errors.PwdShort;
23873             return false;
23874         }
23875         if (value.length > 16) {
23876             this.markInvalid(this.errors.PwdLong);
23877             this.errorMsg = this.errors.PwdLong;
23878             return false;
23879         }
23880         var strength;
23881         if (this.ClientSideStrongPassword(value)) {
23882             strength = 3;
23883         } else if (this.ClientSideMediumPassword(value)) {
23884             strength = 2;
23885         } else if (this.ClientSideWeakPassword(value)) {
23886             strength = 1;
23887         } else {
23888             strength = 0;
23889         }
23890
23891         
23892         if (strength < 2) {
23893             //this.markInvalid(this.errors.TooWeak);
23894             this.errorMsg = this.errors.TooWeak;
23895             //return false;
23896         }
23897         
23898         
23899         console.log('strength2: ' + strength);
23900         
23901         //var pm = this.trigger.child('div/div/div').dom;
23902         
23903         var pm = this.trigger.child('div/div');
23904         pm.removeClass(this.meterClass);
23905         pm.addClass(this.meterClass[strength]);
23906                 
23907         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23908                 
23909         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23910         
23911         this.errorMsg = ''; 
23912         return true;
23913     },
23914     // private
23915     CharacterSetChecks: function (type)
23916     {
23917         this.type = type;
23918         this.fResult = false;
23919     },
23920     // private
23921     isctype: function (character, type)
23922     {
23923         switch (type) {  
23924             case this.kCapitalLetter:
23925                 if (character >= 'A' && character <= 'Z') {
23926                     return true;
23927                 }
23928                 break;
23929             
23930             case this.kSmallLetter:
23931                 if (character >= 'a' && character <= 'z') {
23932                     return true;
23933                 }
23934                 break;
23935             
23936             case this.kDigit:
23937                 if (character >= '0' && character <= '9') {
23938                     return true;
23939                 }
23940                 break;
23941             
23942             case this.kPunctuation:
23943                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23944                     return true;
23945                 }
23946                 break;
23947             
23948             default:
23949                 return false;
23950         }
23951
23952     },
23953     // private
23954     IsLongEnough: function (pwd, size)
23955     {
23956         return !(pwd == null || isNaN(size) || pwd.length < size);
23957     },
23958     // private
23959     SpansEnoughCharacterSets: function (word, nb)
23960     {
23961         if (!this.IsLongEnough(word, nb))
23962         {
23963             return false;
23964         }
23965
23966         var characterSetChecks = new Array(
23967             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23968             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23969         );
23970         
23971         for (var index = 0; index < word.length; ++index) {
23972             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23973                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23974                     characterSetChecks[nCharSet].fResult = true;
23975                     break;
23976                 }
23977             }
23978         }
23979
23980         var nCharSets = 0;
23981         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23982             if (characterSetChecks[nCharSet].fResult) {
23983                 ++nCharSets;
23984             }
23985         }
23986
23987         if (nCharSets < nb) {
23988             return false;
23989         }
23990         return true;
23991     },
23992     // private
23993     ClientSideStrongPassword: function (pwd)
23994     {
23995         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23996     },
23997     // private
23998     ClientSideMediumPassword: function (pwd)
23999     {
24000         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24001     },
24002     // private
24003     ClientSideWeakPassword: function (pwd)
24004     {
24005         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24006     }
24007           
24008 })//<script type="text/javascript">
24009
24010 /*
24011  * Based  Ext JS Library 1.1.1
24012  * Copyright(c) 2006-2007, Ext JS, LLC.
24013  * LGPL
24014  *
24015  */
24016  
24017 /**
24018  * @class Roo.HtmlEditorCore
24019  * @extends Roo.Component
24020  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24021  *
24022  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24023  */
24024
24025 Roo.HtmlEditorCore = function(config){
24026     
24027     
24028     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24029     
24030     
24031     this.addEvents({
24032         /**
24033          * @event initialize
24034          * Fires when the editor is fully initialized (including the iframe)
24035          * @param {Roo.HtmlEditorCore} this
24036          */
24037         initialize: true,
24038         /**
24039          * @event activate
24040          * Fires when the editor is first receives the focus. Any insertion must wait
24041          * until after this event.
24042          * @param {Roo.HtmlEditorCore} this
24043          */
24044         activate: true,
24045          /**
24046          * @event beforesync
24047          * Fires before the textarea is updated with content from the editor iframe. Return false
24048          * to cancel the sync.
24049          * @param {Roo.HtmlEditorCore} this
24050          * @param {String} html
24051          */
24052         beforesync: true,
24053          /**
24054          * @event beforepush
24055          * Fires before the iframe editor is updated with content from the textarea. Return false
24056          * to cancel the push.
24057          * @param {Roo.HtmlEditorCore} this
24058          * @param {String} html
24059          */
24060         beforepush: true,
24061          /**
24062          * @event sync
24063          * Fires when the textarea is updated with content from the editor iframe.
24064          * @param {Roo.HtmlEditorCore} this
24065          * @param {String} html
24066          */
24067         sync: true,
24068          /**
24069          * @event push
24070          * Fires when the iframe editor is updated with content from the textarea.
24071          * @param {Roo.HtmlEditorCore} this
24072          * @param {String} html
24073          */
24074         push: true,
24075         
24076         /**
24077          * @event editorevent
24078          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24079          * @param {Roo.HtmlEditorCore} this
24080          */
24081         editorevent: true
24082         
24083     });
24084     
24085     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24086     
24087     // defaults : white / black...
24088     this.applyBlacklists();
24089     
24090     
24091     
24092 };
24093
24094
24095 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24096
24097
24098      /**
24099      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24100      */
24101     
24102     owner : false,
24103     
24104      /**
24105      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24106      *                        Roo.resizable.
24107      */
24108     resizable : false,
24109      /**
24110      * @cfg {Number} height (in pixels)
24111      */   
24112     height: 300,
24113    /**
24114      * @cfg {Number} width (in pixels)
24115      */   
24116     width: 500,
24117     
24118     /**
24119      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24120      * 
24121      */
24122     stylesheets: false,
24123     
24124     // id of frame..
24125     frameId: false,
24126     
24127     // private properties
24128     validationEvent : false,
24129     deferHeight: true,
24130     initialized : false,
24131     activated : false,
24132     sourceEditMode : false,
24133     onFocus : Roo.emptyFn,
24134     iframePad:3,
24135     hideMode:'offsets',
24136     
24137     clearUp: true,
24138     
24139     // blacklist + whitelisted elements..
24140     black: false,
24141     white: false,
24142      
24143     bodyCls : '',
24144
24145     /**
24146      * Protected method that will not generally be called directly. It
24147      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24148      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24149      */
24150     getDocMarkup : function(){
24151         // body styles..
24152         var st = '';
24153         
24154         // inherit styels from page...?? 
24155         if (this.stylesheets === false) {
24156             
24157             Roo.get(document.head).select('style').each(function(node) {
24158                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24159             });
24160             
24161             Roo.get(document.head).select('link').each(function(node) { 
24162                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24163             });
24164             
24165         } else if (!this.stylesheets.length) {
24166                 // simple..
24167                 st = '<style type="text/css">' +
24168                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24169                    '</style>';
24170         } else {
24171             for (var i in this.stylesheets) { 
24172                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24173             }
24174             
24175         }
24176         
24177         st +=  '<style type="text/css">' +
24178             'IMG { cursor: pointer } ' +
24179         '</style>';
24180
24181         var cls = 'roo-htmleditor-body';
24182         
24183         if(this.bodyCls.length){
24184             cls += ' ' + this.bodyCls;
24185         }
24186         
24187         return '<html><head>' + st  +
24188             //<style type="text/css">' +
24189             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24190             //'</style>' +
24191             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24192     },
24193
24194     // private
24195     onRender : function(ct, position)
24196     {
24197         var _t = this;
24198         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24199         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24200         
24201         
24202         this.el.dom.style.border = '0 none';
24203         this.el.dom.setAttribute('tabIndex', -1);
24204         this.el.addClass('x-hidden hide');
24205         
24206         
24207         
24208         if(Roo.isIE){ // fix IE 1px bogus margin
24209             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24210         }
24211        
24212         
24213         this.frameId = Roo.id();
24214         
24215          
24216         
24217         var iframe = this.owner.wrap.createChild({
24218             tag: 'iframe',
24219             cls: 'form-control', // bootstrap..
24220             id: this.frameId,
24221             name: this.frameId,
24222             frameBorder : 'no',
24223             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24224         }, this.el
24225         );
24226         
24227         
24228         this.iframe = iframe.dom;
24229
24230          this.assignDocWin();
24231         
24232         this.doc.designMode = 'on';
24233        
24234         this.doc.open();
24235         this.doc.write(this.getDocMarkup());
24236         this.doc.close();
24237
24238         
24239         var task = { // must defer to wait for browser to be ready
24240             run : function(){
24241                 //console.log("run task?" + this.doc.readyState);
24242                 this.assignDocWin();
24243                 if(this.doc.body || this.doc.readyState == 'complete'){
24244                     try {
24245                         this.doc.designMode="on";
24246                     } catch (e) {
24247                         return;
24248                     }
24249                     Roo.TaskMgr.stop(task);
24250                     this.initEditor.defer(10, this);
24251                 }
24252             },
24253             interval : 10,
24254             duration: 10000,
24255             scope: this
24256         };
24257         Roo.TaskMgr.start(task);
24258
24259     },
24260
24261     // private
24262     onResize : function(w, h)
24263     {
24264          Roo.log('resize: ' +w + ',' + h );
24265         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24266         if(!this.iframe){
24267             return;
24268         }
24269         if(typeof w == 'number'){
24270             
24271             this.iframe.style.width = w + 'px';
24272         }
24273         if(typeof h == 'number'){
24274             
24275             this.iframe.style.height = h + 'px';
24276             if(this.doc){
24277                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24278             }
24279         }
24280         
24281     },
24282
24283     /**
24284      * Toggles the editor between standard and source edit mode.
24285      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24286      */
24287     toggleSourceEdit : function(sourceEditMode){
24288         
24289         this.sourceEditMode = sourceEditMode === true;
24290         
24291         if(this.sourceEditMode){
24292  
24293             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24294             
24295         }else{
24296             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24297             //this.iframe.className = '';
24298             this.deferFocus();
24299         }
24300         //this.setSize(this.owner.wrap.getSize());
24301         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24302     },
24303
24304     
24305   
24306
24307     /**
24308      * Protected method that will not generally be called directly. If you need/want
24309      * custom HTML cleanup, this is the method you should override.
24310      * @param {String} html The HTML to be cleaned
24311      * return {String} The cleaned HTML
24312      */
24313     cleanHtml : function(html){
24314         html = String(html);
24315         if(html.length > 5){
24316             if(Roo.isSafari){ // strip safari nonsense
24317                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24318             }
24319         }
24320         if(html == '&nbsp;'){
24321             html = '';
24322         }
24323         return html;
24324     },
24325
24326     /**
24327      * HTML Editor -> Textarea
24328      * Protected method that will not generally be called directly. Syncs the contents
24329      * of the editor iframe with the textarea.
24330      */
24331     syncValue : function(){
24332         if(this.initialized){
24333             var bd = (this.doc.body || this.doc.documentElement);
24334             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24335             var html = bd.innerHTML;
24336             if(Roo.isSafari){
24337                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24338                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24339                 if(m && m[1]){
24340                     html = '<div style="'+m[0]+'">' + html + '</div>';
24341                 }
24342             }
24343             html = this.cleanHtml(html);
24344             // fix up the special chars.. normaly like back quotes in word...
24345             // however we do not want to do this with chinese..
24346             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24347                 
24348                 var cc = match.charCodeAt();
24349
24350                 // Get the character value, handling surrogate pairs
24351                 if (match.length == 2) {
24352                     // It's a surrogate pair, calculate the Unicode code point
24353                     var high = match.charCodeAt(0) - 0xD800;
24354                     var low  = match.charCodeAt(1) - 0xDC00;
24355                     cc = (high * 0x400) + low + 0x10000;
24356                 }  else if (
24357                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24358                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24359                     (cc >= 0xf900 && cc < 0xfb00 )
24360                 ) {
24361                         return match;
24362                 }  
24363          
24364                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24365                 return "&#" + cc + ";";
24366                 
24367                 
24368             });
24369             
24370             
24371              
24372             if(this.owner.fireEvent('beforesync', this, html) !== false){
24373                 this.el.dom.value = html;
24374                 this.owner.fireEvent('sync', this, html);
24375             }
24376         }
24377     },
24378
24379     /**
24380      * Protected method that will not generally be called directly. Pushes the value of the textarea
24381      * into the iframe editor.
24382      */
24383     pushValue : function(){
24384         if(this.initialized){
24385             var v = this.el.dom.value.trim();
24386             
24387 //            if(v.length < 1){
24388 //                v = '&#160;';
24389 //            }
24390             
24391             if(this.owner.fireEvent('beforepush', this, v) !== false){
24392                 var d = (this.doc.body || this.doc.documentElement);
24393                 d.innerHTML = v;
24394                 this.cleanUpPaste();
24395                 this.el.dom.value = d.innerHTML;
24396                 this.owner.fireEvent('push', this, v);
24397             }
24398         }
24399     },
24400
24401     // private
24402     deferFocus : function(){
24403         this.focus.defer(10, this);
24404     },
24405
24406     // doc'ed in Field
24407     focus : function(){
24408         if(this.win && !this.sourceEditMode){
24409             this.win.focus();
24410         }else{
24411             this.el.focus();
24412         }
24413     },
24414     
24415     assignDocWin: function()
24416     {
24417         var iframe = this.iframe;
24418         
24419          if(Roo.isIE){
24420             this.doc = iframe.contentWindow.document;
24421             this.win = iframe.contentWindow;
24422         } else {
24423 //            if (!Roo.get(this.frameId)) {
24424 //                return;
24425 //            }
24426 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24427 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24428             
24429             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24430                 return;
24431             }
24432             
24433             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24434             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24435         }
24436     },
24437     
24438     // private
24439     initEditor : function(){
24440         //console.log("INIT EDITOR");
24441         this.assignDocWin();
24442         
24443         
24444         
24445         this.doc.designMode="on";
24446         this.doc.open();
24447         this.doc.write(this.getDocMarkup());
24448         this.doc.close();
24449         
24450         var dbody = (this.doc.body || this.doc.documentElement);
24451         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24452         // this copies styles from the containing element into thsi one..
24453         // not sure why we need all of this..
24454         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24455         
24456         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24457         //ss['background-attachment'] = 'fixed'; // w3c
24458         dbody.bgProperties = 'fixed'; // ie
24459         //Roo.DomHelper.applyStyles(dbody, ss);
24460         Roo.EventManager.on(this.doc, {
24461             //'mousedown': this.onEditorEvent,
24462             'mouseup': this.onEditorEvent,
24463             'dblclick': this.onEditorEvent,
24464             'click': this.onEditorEvent,
24465             'keyup': this.onEditorEvent,
24466             buffer:100,
24467             scope: this
24468         });
24469         if(Roo.isGecko){
24470             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24471         }
24472         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24473             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24474         }
24475         this.initialized = true;
24476
24477         this.owner.fireEvent('initialize', this);
24478         this.pushValue();
24479     },
24480
24481     // private
24482     onDestroy : function(){
24483         
24484         
24485         
24486         if(this.rendered){
24487             
24488             //for (var i =0; i < this.toolbars.length;i++) {
24489             //    // fixme - ask toolbars for heights?
24490             //    this.toolbars[i].onDestroy();
24491            // }
24492             
24493             //this.wrap.dom.innerHTML = '';
24494             //this.wrap.remove();
24495         }
24496     },
24497
24498     // private
24499     onFirstFocus : function(){
24500         
24501         this.assignDocWin();
24502         
24503         
24504         this.activated = true;
24505          
24506     
24507         if(Roo.isGecko){ // prevent silly gecko errors
24508             this.win.focus();
24509             var s = this.win.getSelection();
24510             if(!s.focusNode || s.focusNode.nodeType != 3){
24511                 var r = s.getRangeAt(0);
24512                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24513                 r.collapse(true);
24514                 this.deferFocus();
24515             }
24516             try{
24517                 this.execCmd('useCSS', true);
24518                 this.execCmd('styleWithCSS', false);
24519             }catch(e){}
24520         }
24521         this.owner.fireEvent('activate', this);
24522     },
24523
24524     // private
24525     adjustFont: function(btn){
24526         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24527         //if(Roo.isSafari){ // safari
24528         //    adjust *= 2;
24529        // }
24530         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24531         if(Roo.isSafari){ // safari
24532             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24533             v =  (v < 10) ? 10 : v;
24534             v =  (v > 48) ? 48 : v;
24535             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24536             
24537         }
24538         
24539         
24540         v = Math.max(1, v+adjust);
24541         
24542         this.execCmd('FontSize', v  );
24543     },
24544
24545     onEditorEvent : function(e)
24546     {
24547         this.owner.fireEvent('editorevent', this, e);
24548       //  this.updateToolbar();
24549         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24550     },
24551
24552     insertTag : function(tg)
24553     {
24554         // could be a bit smarter... -> wrap the current selected tRoo..
24555         if (tg.toLowerCase() == 'span' ||
24556             tg.toLowerCase() == 'code' ||
24557             tg.toLowerCase() == 'sup' ||
24558             tg.toLowerCase() == 'sub' 
24559             ) {
24560             
24561             range = this.createRange(this.getSelection());
24562             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24563             wrappingNode.appendChild(range.extractContents());
24564             range.insertNode(wrappingNode);
24565
24566             return;
24567             
24568             
24569             
24570         }
24571         this.execCmd("formatblock",   tg);
24572         
24573     },
24574     
24575     insertText : function(txt)
24576     {
24577         
24578         
24579         var range = this.createRange();
24580         range.deleteContents();
24581                //alert(Sender.getAttribute('label'));
24582                
24583         range.insertNode(this.doc.createTextNode(txt));
24584     } ,
24585     
24586      
24587
24588     /**
24589      * Executes a Midas editor command on the editor document and performs necessary focus and
24590      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24591      * @param {String} cmd The Midas command
24592      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24593      */
24594     relayCmd : function(cmd, value){
24595         this.win.focus();
24596         this.execCmd(cmd, value);
24597         this.owner.fireEvent('editorevent', this);
24598         //this.updateToolbar();
24599         this.owner.deferFocus();
24600     },
24601
24602     /**
24603      * Executes a Midas editor command directly on the editor document.
24604      * For visual commands, you should use {@link #relayCmd} instead.
24605      * <b>This should only be called after the editor is initialized.</b>
24606      * @param {String} cmd The Midas command
24607      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24608      */
24609     execCmd : function(cmd, value){
24610         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24611         this.syncValue();
24612     },
24613  
24614  
24615    
24616     /**
24617      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24618      * to insert tRoo.
24619      * @param {String} text | dom node.. 
24620      */
24621     insertAtCursor : function(text)
24622     {
24623         
24624         if(!this.activated){
24625             return;
24626         }
24627         /*
24628         if(Roo.isIE){
24629             this.win.focus();
24630             var r = this.doc.selection.createRange();
24631             if(r){
24632                 r.collapse(true);
24633                 r.pasteHTML(text);
24634                 this.syncValue();
24635                 this.deferFocus();
24636             
24637             }
24638             return;
24639         }
24640         */
24641         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24642             this.win.focus();
24643             
24644             
24645             // from jquery ui (MIT licenced)
24646             var range, node;
24647             var win = this.win;
24648             
24649             if (win.getSelection && win.getSelection().getRangeAt) {
24650                 range = win.getSelection().getRangeAt(0);
24651                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24652                 range.insertNode(node);
24653             } else if (win.document.selection && win.document.selection.createRange) {
24654                 // no firefox support
24655                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24656                 win.document.selection.createRange().pasteHTML(txt);
24657             } else {
24658                 // no firefox support
24659                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24660                 this.execCmd('InsertHTML', txt);
24661             } 
24662             
24663             this.syncValue();
24664             
24665             this.deferFocus();
24666         }
24667     },
24668  // private
24669     mozKeyPress : function(e){
24670         if(e.ctrlKey){
24671             var c = e.getCharCode(), cmd;
24672           
24673             if(c > 0){
24674                 c = String.fromCharCode(c).toLowerCase();
24675                 switch(c){
24676                     case 'b':
24677                         cmd = 'bold';
24678                         break;
24679                     case 'i':
24680                         cmd = 'italic';
24681                         break;
24682                     
24683                     case 'u':
24684                         cmd = 'underline';
24685                         break;
24686                     
24687                     case 'v':
24688                         this.cleanUpPaste.defer(100, this);
24689                         return;
24690                         
24691                 }
24692                 if(cmd){
24693                     this.win.focus();
24694                     this.execCmd(cmd);
24695                     this.deferFocus();
24696                     e.preventDefault();
24697                 }
24698                 
24699             }
24700         }
24701     },
24702
24703     // private
24704     fixKeys : function(){ // load time branching for fastest keydown performance
24705         if(Roo.isIE){
24706             return function(e){
24707                 var k = e.getKey(), r;
24708                 if(k == e.TAB){
24709                     e.stopEvent();
24710                     r = this.doc.selection.createRange();
24711                     if(r){
24712                         r.collapse(true);
24713                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24714                         this.deferFocus();
24715                     }
24716                     return;
24717                 }
24718                 
24719                 if(k == e.ENTER){
24720                     r = this.doc.selection.createRange();
24721                     if(r){
24722                         var target = r.parentElement();
24723                         if(!target || target.tagName.toLowerCase() != 'li'){
24724                             e.stopEvent();
24725                             r.pasteHTML('<br />');
24726                             r.collapse(false);
24727                             r.select();
24728                         }
24729                     }
24730                 }
24731                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24732                     this.cleanUpPaste.defer(100, this);
24733                     return;
24734                 }
24735                 
24736                 
24737             };
24738         }else if(Roo.isOpera){
24739             return function(e){
24740                 var k = e.getKey();
24741                 if(k == e.TAB){
24742                     e.stopEvent();
24743                     this.win.focus();
24744                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24745                     this.deferFocus();
24746                 }
24747                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24748                     this.cleanUpPaste.defer(100, this);
24749                     return;
24750                 }
24751                 
24752             };
24753         }else if(Roo.isSafari){
24754             return function(e){
24755                 var k = e.getKey();
24756                 
24757                 if(k == e.TAB){
24758                     e.stopEvent();
24759                     this.execCmd('InsertText','\t');
24760                     this.deferFocus();
24761                     return;
24762                 }
24763                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24764                     this.cleanUpPaste.defer(100, this);
24765                     return;
24766                 }
24767                 
24768              };
24769         }
24770     }(),
24771     
24772     getAllAncestors: function()
24773     {
24774         var p = this.getSelectedNode();
24775         var a = [];
24776         if (!p) {
24777             a.push(p); // push blank onto stack..
24778             p = this.getParentElement();
24779         }
24780         
24781         
24782         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24783             a.push(p);
24784             p = p.parentNode;
24785         }
24786         a.push(this.doc.body);
24787         return a;
24788     },
24789     lastSel : false,
24790     lastSelNode : false,
24791     
24792     
24793     getSelection : function() 
24794     {
24795         this.assignDocWin();
24796         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24797     },
24798     
24799     getSelectedNode: function() 
24800     {
24801         // this may only work on Gecko!!!
24802         
24803         // should we cache this!!!!
24804         
24805         
24806         
24807          
24808         var range = this.createRange(this.getSelection()).cloneRange();
24809         
24810         if (Roo.isIE) {
24811             var parent = range.parentElement();
24812             while (true) {
24813                 var testRange = range.duplicate();
24814                 testRange.moveToElementText(parent);
24815                 if (testRange.inRange(range)) {
24816                     break;
24817                 }
24818                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24819                     break;
24820                 }
24821                 parent = parent.parentElement;
24822             }
24823             return parent;
24824         }
24825         
24826         // is ancestor a text element.
24827         var ac =  range.commonAncestorContainer;
24828         if (ac.nodeType == 3) {
24829             ac = ac.parentNode;
24830         }
24831         
24832         var ar = ac.childNodes;
24833          
24834         var nodes = [];
24835         var other_nodes = [];
24836         var has_other_nodes = false;
24837         for (var i=0;i<ar.length;i++) {
24838             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24839                 continue;
24840             }
24841             // fullly contained node.
24842             
24843             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24844                 nodes.push(ar[i]);
24845                 continue;
24846             }
24847             
24848             // probably selected..
24849             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24850                 other_nodes.push(ar[i]);
24851                 continue;
24852             }
24853             // outer..
24854             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24855                 continue;
24856             }
24857             
24858             
24859             has_other_nodes = true;
24860         }
24861         if (!nodes.length && other_nodes.length) {
24862             nodes= other_nodes;
24863         }
24864         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24865             return false;
24866         }
24867         
24868         return nodes[0];
24869     },
24870     createRange: function(sel)
24871     {
24872         // this has strange effects when using with 
24873         // top toolbar - not sure if it's a great idea.
24874         //this.editor.contentWindow.focus();
24875         if (typeof sel != "undefined") {
24876             try {
24877                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24878             } catch(e) {
24879                 return this.doc.createRange();
24880             }
24881         } else {
24882             return this.doc.createRange();
24883         }
24884     },
24885     getParentElement: function()
24886     {
24887         
24888         this.assignDocWin();
24889         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24890         
24891         var range = this.createRange(sel);
24892          
24893         try {
24894             var p = range.commonAncestorContainer;
24895             while (p.nodeType == 3) { // text node
24896                 p = p.parentNode;
24897             }
24898             return p;
24899         } catch (e) {
24900             return null;
24901         }
24902     
24903     },
24904     /***
24905      *
24906      * Range intersection.. the hard stuff...
24907      *  '-1' = before
24908      *  '0' = hits..
24909      *  '1' = after.
24910      *         [ -- selected range --- ]
24911      *   [fail]                        [fail]
24912      *
24913      *    basically..
24914      *      if end is before start or  hits it. fail.
24915      *      if start is after end or hits it fail.
24916      *
24917      *   if either hits (but other is outside. - then it's not 
24918      *   
24919      *    
24920      **/
24921     
24922     
24923     // @see http://www.thismuchiknow.co.uk/?p=64.
24924     rangeIntersectsNode : function(range, node)
24925     {
24926         var nodeRange = node.ownerDocument.createRange();
24927         try {
24928             nodeRange.selectNode(node);
24929         } catch (e) {
24930             nodeRange.selectNodeContents(node);
24931         }
24932     
24933         var rangeStartRange = range.cloneRange();
24934         rangeStartRange.collapse(true);
24935     
24936         var rangeEndRange = range.cloneRange();
24937         rangeEndRange.collapse(false);
24938     
24939         var nodeStartRange = nodeRange.cloneRange();
24940         nodeStartRange.collapse(true);
24941     
24942         var nodeEndRange = nodeRange.cloneRange();
24943         nodeEndRange.collapse(false);
24944     
24945         return rangeStartRange.compareBoundaryPoints(
24946                  Range.START_TO_START, nodeEndRange) == -1 &&
24947                rangeEndRange.compareBoundaryPoints(
24948                  Range.START_TO_START, nodeStartRange) == 1;
24949         
24950          
24951     },
24952     rangeCompareNode : function(range, node)
24953     {
24954         var nodeRange = node.ownerDocument.createRange();
24955         try {
24956             nodeRange.selectNode(node);
24957         } catch (e) {
24958             nodeRange.selectNodeContents(node);
24959         }
24960         
24961         
24962         range.collapse(true);
24963     
24964         nodeRange.collapse(true);
24965      
24966         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24967         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24968          
24969         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24970         
24971         var nodeIsBefore   =  ss == 1;
24972         var nodeIsAfter    = ee == -1;
24973         
24974         if (nodeIsBefore && nodeIsAfter) {
24975             return 0; // outer
24976         }
24977         if (!nodeIsBefore && nodeIsAfter) {
24978             return 1; //right trailed.
24979         }
24980         
24981         if (nodeIsBefore && !nodeIsAfter) {
24982             return 2;  // left trailed.
24983         }
24984         // fully contined.
24985         return 3;
24986     },
24987
24988     // private? - in a new class?
24989     cleanUpPaste :  function()
24990     {
24991         // cleans up the whole document..
24992         Roo.log('cleanuppaste');
24993         
24994         this.cleanUpChildren(this.doc.body);
24995         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24996         if (clean != this.doc.body.innerHTML) {
24997             this.doc.body.innerHTML = clean;
24998         }
24999         
25000     },
25001     
25002     cleanWordChars : function(input) {// change the chars to hex code
25003         var he = Roo.HtmlEditorCore;
25004         
25005         var output = input;
25006         Roo.each(he.swapCodes, function(sw) { 
25007             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25008             
25009             output = output.replace(swapper, sw[1]);
25010         });
25011         
25012         return output;
25013     },
25014     
25015     
25016     cleanUpChildren : function (n)
25017     {
25018         if (!n.childNodes.length) {
25019             return;
25020         }
25021         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25022            this.cleanUpChild(n.childNodes[i]);
25023         }
25024     },
25025     
25026     
25027         
25028     
25029     cleanUpChild : function (node)
25030     {
25031         var ed = this;
25032         //console.log(node);
25033         if (node.nodeName == "#text") {
25034             // clean up silly Windows -- stuff?
25035             return; 
25036         }
25037         if (node.nodeName == "#comment") {
25038             node.parentNode.removeChild(node);
25039             // clean up silly Windows -- stuff?
25040             return; 
25041         }
25042         var lcname = node.tagName.toLowerCase();
25043         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25044         // whitelist of tags..
25045         
25046         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25047             // remove node.
25048             node.parentNode.removeChild(node);
25049             return;
25050             
25051         }
25052         
25053         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25054         
25055         // spans with no attributes - just remove them..
25056         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25057             remove_keep_children = true;
25058         }
25059         
25060         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25061         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25062         
25063         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25064         //    remove_keep_children = true;
25065         //}
25066         
25067         if (remove_keep_children) {
25068             this.cleanUpChildren(node);
25069             // inserts everything just before this node...
25070             while (node.childNodes.length) {
25071                 var cn = node.childNodes[0];
25072                 node.removeChild(cn);
25073                 node.parentNode.insertBefore(cn, node);
25074             }
25075             node.parentNode.removeChild(node);
25076             return;
25077         }
25078         
25079         if (!node.attributes || !node.attributes.length) {
25080             
25081           
25082             
25083             
25084             this.cleanUpChildren(node);
25085             return;
25086         }
25087         
25088         function cleanAttr(n,v)
25089         {
25090             
25091             if (v.match(/^\./) || v.match(/^\//)) {
25092                 return;
25093             }
25094             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25095                 return;
25096             }
25097             if (v.match(/^#/)) {
25098                 return;
25099             }
25100             if (v.match(/^\{/)) { // allow template editing.
25101                 return;
25102             }
25103 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25104             node.removeAttribute(n);
25105             
25106         }
25107         
25108         var cwhite = this.cwhite;
25109         var cblack = this.cblack;
25110             
25111         function cleanStyle(n,v)
25112         {
25113             if (v.match(/expression/)) { //XSS?? should we even bother..
25114                 node.removeAttribute(n);
25115                 return;
25116             }
25117             
25118             var parts = v.split(/;/);
25119             var clean = [];
25120             
25121             Roo.each(parts, function(p) {
25122                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25123                 if (!p.length) {
25124                     return true;
25125                 }
25126                 var l = p.split(':').shift().replace(/\s+/g,'');
25127                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25128                 
25129                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25130 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25131                     //node.removeAttribute(n);
25132                     return true;
25133                 }
25134                 //Roo.log()
25135                 // only allow 'c whitelisted system attributes'
25136                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25137 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25138                     //node.removeAttribute(n);
25139                     return true;
25140                 }
25141                 
25142                 
25143                  
25144                 
25145                 clean.push(p);
25146                 return true;
25147             });
25148             if (clean.length) { 
25149                 node.setAttribute(n, clean.join(';'));
25150             } else {
25151                 node.removeAttribute(n);
25152             }
25153             
25154         }
25155         
25156         
25157         for (var i = node.attributes.length-1; i > -1 ; i--) {
25158             var a = node.attributes[i];
25159             //console.log(a);
25160             
25161             if (a.name.toLowerCase().substr(0,2)=='on')  {
25162                 node.removeAttribute(a.name);
25163                 continue;
25164             }
25165             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25166                 node.removeAttribute(a.name);
25167                 continue;
25168             }
25169             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25170                 cleanAttr(a.name,a.value); // fixme..
25171                 continue;
25172             }
25173             if (a.name == 'style') {
25174                 cleanStyle(a.name,a.value);
25175                 continue;
25176             }
25177             /// clean up MS crap..
25178             // tecnically this should be a list of valid class'es..
25179             
25180             
25181             if (a.name == 'class') {
25182                 if (a.value.match(/^Mso/)) {
25183                     node.removeAttribute('class');
25184                 }
25185                 
25186                 if (a.value.match(/^body$/)) {
25187                     node.removeAttribute('class');
25188                 }
25189                 continue;
25190             }
25191             
25192             // style cleanup!?
25193             // class cleanup?
25194             
25195         }
25196         
25197         
25198         this.cleanUpChildren(node);
25199         
25200         
25201     },
25202     
25203     /**
25204      * Clean up MS wordisms...
25205      */
25206     cleanWord : function(node)
25207     {
25208         if (!node) {
25209             this.cleanWord(this.doc.body);
25210             return;
25211         }
25212         
25213         if(
25214                 node.nodeName == 'SPAN' &&
25215                 !node.hasAttributes() &&
25216                 node.childNodes.length == 1 &&
25217                 node.firstChild.nodeName == "#text"  
25218         ) {
25219             var textNode = node.firstChild;
25220             node.removeChild(textNode);
25221             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25222                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25223             }
25224             node.parentNode.insertBefore(textNode, node);
25225             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25226                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25227             }
25228             node.parentNode.removeChild(node);
25229         }
25230         
25231         if (node.nodeName == "#text") {
25232             // clean up silly Windows -- stuff?
25233             return; 
25234         }
25235         if (node.nodeName == "#comment") {
25236             node.parentNode.removeChild(node);
25237             // clean up silly Windows -- stuff?
25238             return; 
25239         }
25240         
25241         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25242             node.parentNode.removeChild(node);
25243             return;
25244         }
25245         //Roo.log(node.tagName);
25246         // remove - but keep children..
25247         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25248             //Roo.log('-- removed');
25249             while (node.childNodes.length) {
25250                 var cn = node.childNodes[0];
25251                 node.removeChild(cn);
25252                 node.parentNode.insertBefore(cn, node);
25253                 // move node to parent - and clean it..
25254                 this.cleanWord(cn);
25255             }
25256             node.parentNode.removeChild(node);
25257             /// no need to iterate chidlren = it's got none..
25258             //this.iterateChildren(node, this.cleanWord);
25259             return;
25260         }
25261         // clean styles
25262         if (node.className.length) {
25263             
25264             var cn = node.className.split(/\W+/);
25265             var cna = [];
25266             Roo.each(cn, function(cls) {
25267                 if (cls.match(/Mso[a-zA-Z]+/)) {
25268                     return;
25269                 }
25270                 cna.push(cls);
25271             });
25272             node.className = cna.length ? cna.join(' ') : '';
25273             if (!cna.length) {
25274                 node.removeAttribute("class");
25275             }
25276         }
25277         
25278         if (node.hasAttribute("lang")) {
25279             node.removeAttribute("lang");
25280         }
25281         
25282         if (node.hasAttribute("style")) {
25283             
25284             var styles = node.getAttribute("style").split(";");
25285             var nstyle = [];
25286             Roo.each(styles, function(s) {
25287                 if (!s.match(/:/)) {
25288                     return;
25289                 }
25290                 var kv = s.split(":");
25291                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25292                     return;
25293                 }
25294                 // what ever is left... we allow.
25295                 nstyle.push(s);
25296             });
25297             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25298             if (!nstyle.length) {
25299                 node.removeAttribute('style');
25300             }
25301         }
25302         this.iterateChildren(node, this.cleanWord);
25303         
25304         
25305         
25306     },
25307     /**
25308      * iterateChildren of a Node, calling fn each time, using this as the scole..
25309      * @param {DomNode} node node to iterate children of.
25310      * @param {Function} fn method of this class to call on each item.
25311      */
25312     iterateChildren : function(node, fn)
25313     {
25314         if (!node.childNodes.length) {
25315                 return;
25316         }
25317         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25318            fn.call(this, node.childNodes[i])
25319         }
25320     },
25321     
25322     
25323     /**
25324      * cleanTableWidths.
25325      *
25326      * Quite often pasting from word etc.. results in tables with column and widths.
25327      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25328      *
25329      */
25330     cleanTableWidths : function(node)
25331     {
25332          
25333          
25334         if (!node) {
25335             this.cleanTableWidths(this.doc.body);
25336             return;
25337         }
25338         
25339         // ignore list...
25340         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25341             return; 
25342         }
25343         Roo.log(node.tagName);
25344         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25345             this.iterateChildren(node, this.cleanTableWidths);
25346             return;
25347         }
25348         if (node.hasAttribute('width')) {
25349             node.removeAttribute('width');
25350         }
25351         
25352          
25353         if (node.hasAttribute("style")) {
25354             // pretty basic...
25355             
25356             var styles = node.getAttribute("style").split(";");
25357             var nstyle = [];
25358             Roo.each(styles, function(s) {
25359                 if (!s.match(/:/)) {
25360                     return;
25361                 }
25362                 var kv = s.split(":");
25363                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25364                     return;
25365                 }
25366                 // what ever is left... we allow.
25367                 nstyle.push(s);
25368             });
25369             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25370             if (!nstyle.length) {
25371                 node.removeAttribute('style');
25372             }
25373         }
25374         
25375         this.iterateChildren(node, this.cleanTableWidths);
25376         
25377         
25378     },
25379     
25380     
25381     
25382     
25383     domToHTML : function(currentElement, depth, nopadtext) {
25384         
25385         depth = depth || 0;
25386         nopadtext = nopadtext || false;
25387     
25388         if (!currentElement) {
25389             return this.domToHTML(this.doc.body);
25390         }
25391         
25392         //Roo.log(currentElement);
25393         var j;
25394         var allText = false;
25395         var nodeName = currentElement.nodeName;
25396         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25397         
25398         if  (nodeName == '#text') {
25399             
25400             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25401         }
25402         
25403         
25404         var ret = '';
25405         if (nodeName != 'BODY') {
25406              
25407             var i = 0;
25408             // Prints the node tagName, such as <A>, <IMG>, etc
25409             if (tagName) {
25410                 var attr = [];
25411                 for(i = 0; i < currentElement.attributes.length;i++) {
25412                     // quoting?
25413                     var aname = currentElement.attributes.item(i).name;
25414                     if (!currentElement.attributes.item(i).value.length) {
25415                         continue;
25416                     }
25417                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25418                 }
25419                 
25420                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25421             } 
25422             else {
25423                 
25424                 // eack
25425             }
25426         } else {
25427             tagName = false;
25428         }
25429         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25430             return ret;
25431         }
25432         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25433             nopadtext = true;
25434         }
25435         
25436         
25437         // Traverse the tree
25438         i = 0;
25439         var currentElementChild = currentElement.childNodes.item(i);
25440         var allText = true;
25441         var innerHTML  = '';
25442         lastnode = '';
25443         while (currentElementChild) {
25444             // Formatting code (indent the tree so it looks nice on the screen)
25445             var nopad = nopadtext;
25446             if (lastnode == 'SPAN') {
25447                 nopad  = true;
25448             }
25449             // text
25450             if  (currentElementChild.nodeName == '#text') {
25451                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25452                 toadd = nopadtext ? toadd : toadd.trim();
25453                 if (!nopad && toadd.length > 80) {
25454                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25455                 }
25456                 innerHTML  += toadd;
25457                 
25458                 i++;
25459                 currentElementChild = currentElement.childNodes.item(i);
25460                 lastNode = '';
25461                 continue;
25462             }
25463             allText = false;
25464             
25465             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25466                 
25467             // Recursively traverse the tree structure of the child node
25468             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25469             lastnode = currentElementChild.nodeName;
25470             i++;
25471             currentElementChild=currentElement.childNodes.item(i);
25472         }
25473         
25474         ret += innerHTML;
25475         
25476         if (!allText) {
25477                 // The remaining code is mostly for formatting the tree
25478             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25479         }
25480         
25481         
25482         if (tagName) {
25483             ret+= "</"+tagName+">";
25484         }
25485         return ret;
25486         
25487     },
25488         
25489     applyBlacklists : function()
25490     {
25491         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25492         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25493         
25494         this.white = [];
25495         this.black = [];
25496         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25497             if (b.indexOf(tag) > -1) {
25498                 return;
25499             }
25500             this.white.push(tag);
25501             
25502         }, this);
25503         
25504         Roo.each(w, function(tag) {
25505             if (b.indexOf(tag) > -1) {
25506                 return;
25507             }
25508             if (this.white.indexOf(tag) > -1) {
25509                 return;
25510             }
25511             this.white.push(tag);
25512             
25513         }, this);
25514         
25515         
25516         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25517             if (w.indexOf(tag) > -1) {
25518                 return;
25519             }
25520             this.black.push(tag);
25521             
25522         }, this);
25523         
25524         Roo.each(b, function(tag) {
25525             if (w.indexOf(tag) > -1) {
25526                 return;
25527             }
25528             if (this.black.indexOf(tag) > -1) {
25529                 return;
25530             }
25531             this.black.push(tag);
25532             
25533         }, this);
25534         
25535         
25536         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25537         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25538         
25539         this.cwhite = [];
25540         this.cblack = [];
25541         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25542             if (b.indexOf(tag) > -1) {
25543                 return;
25544             }
25545             this.cwhite.push(tag);
25546             
25547         }, this);
25548         
25549         Roo.each(w, function(tag) {
25550             if (b.indexOf(tag) > -1) {
25551                 return;
25552             }
25553             if (this.cwhite.indexOf(tag) > -1) {
25554                 return;
25555             }
25556             this.cwhite.push(tag);
25557             
25558         }, this);
25559         
25560         
25561         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25562             if (w.indexOf(tag) > -1) {
25563                 return;
25564             }
25565             this.cblack.push(tag);
25566             
25567         }, this);
25568         
25569         Roo.each(b, function(tag) {
25570             if (w.indexOf(tag) > -1) {
25571                 return;
25572             }
25573             if (this.cblack.indexOf(tag) > -1) {
25574                 return;
25575             }
25576             this.cblack.push(tag);
25577             
25578         }, this);
25579     },
25580     
25581     setStylesheets : function(stylesheets)
25582     {
25583         if(typeof(stylesheets) == 'string'){
25584             Roo.get(this.iframe.contentDocument.head).createChild({
25585                 tag : 'link',
25586                 rel : 'stylesheet',
25587                 type : 'text/css',
25588                 href : stylesheets
25589             });
25590             
25591             return;
25592         }
25593         var _this = this;
25594      
25595         Roo.each(stylesheets, function(s) {
25596             if(!s.length){
25597                 return;
25598             }
25599             
25600             Roo.get(_this.iframe.contentDocument.head).createChild({
25601                 tag : 'link',
25602                 rel : 'stylesheet',
25603                 type : 'text/css',
25604                 href : s
25605             });
25606         });
25607
25608         
25609     },
25610     
25611     removeStylesheets : function()
25612     {
25613         var _this = this;
25614         
25615         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25616             s.remove();
25617         });
25618     },
25619     
25620     setStyle : function(style)
25621     {
25622         Roo.get(this.iframe.contentDocument.head).createChild({
25623             tag : 'style',
25624             type : 'text/css',
25625             html : style
25626         });
25627
25628         return;
25629     }
25630     
25631     // hide stuff that is not compatible
25632     /**
25633      * @event blur
25634      * @hide
25635      */
25636     /**
25637      * @event change
25638      * @hide
25639      */
25640     /**
25641      * @event focus
25642      * @hide
25643      */
25644     /**
25645      * @event specialkey
25646      * @hide
25647      */
25648     /**
25649      * @cfg {String} fieldClass @hide
25650      */
25651     /**
25652      * @cfg {String} focusClass @hide
25653      */
25654     /**
25655      * @cfg {String} autoCreate @hide
25656      */
25657     /**
25658      * @cfg {String} inputType @hide
25659      */
25660     /**
25661      * @cfg {String} invalidClass @hide
25662      */
25663     /**
25664      * @cfg {String} invalidText @hide
25665      */
25666     /**
25667      * @cfg {String} msgFx @hide
25668      */
25669     /**
25670      * @cfg {String} validateOnBlur @hide
25671      */
25672 });
25673
25674 Roo.HtmlEditorCore.white = [
25675         'area', 'br', 'img', 'input', 'hr', 'wbr',
25676         
25677        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25678        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25679        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25680        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25681        'table',   'ul',         'xmp', 
25682        
25683        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25684       'thead',   'tr', 
25685      
25686       'dir', 'menu', 'ol', 'ul', 'dl',
25687        
25688       'embed',  'object'
25689 ];
25690
25691
25692 Roo.HtmlEditorCore.black = [
25693     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25694         'applet', // 
25695         'base',   'basefont', 'bgsound', 'blink',  'body', 
25696         'frame',  'frameset', 'head',    'html',   'ilayer', 
25697         'iframe', 'layer',  'link',     'meta',    'object',   
25698         'script', 'style' ,'title',  'xml' // clean later..
25699 ];
25700 Roo.HtmlEditorCore.clean = [
25701     'script', 'style', 'title', 'xml'
25702 ];
25703 Roo.HtmlEditorCore.remove = [
25704     'font'
25705 ];
25706 // attributes..
25707
25708 Roo.HtmlEditorCore.ablack = [
25709     'on'
25710 ];
25711     
25712 Roo.HtmlEditorCore.aclean = [ 
25713     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25714 ];
25715
25716 // protocols..
25717 Roo.HtmlEditorCore.pwhite= [
25718         'http',  'https',  'mailto'
25719 ];
25720
25721 // white listed style attributes.
25722 Roo.HtmlEditorCore.cwhite= [
25723       //  'text-align', /// default is to allow most things..
25724       
25725          
25726 //        'font-size'//??
25727 ];
25728
25729 // black listed style attributes.
25730 Roo.HtmlEditorCore.cblack= [
25731       //  'font-size' -- this can be set by the project 
25732 ];
25733
25734
25735 Roo.HtmlEditorCore.swapCodes   =[ 
25736     [    8211, "--" ], 
25737     [    8212, "--" ], 
25738     [    8216,  "'" ],  
25739     [    8217, "'" ],  
25740     [    8220, '"' ],  
25741     [    8221, '"' ],  
25742     [    8226, "*" ],  
25743     [    8230, "..." ]
25744 ]; 
25745
25746     /*
25747  * - LGPL
25748  *
25749  * HtmlEditor
25750  * 
25751  */
25752
25753 /**
25754  * @class Roo.bootstrap.HtmlEditor
25755  * @extends Roo.bootstrap.TextArea
25756  * Bootstrap HtmlEditor class
25757
25758  * @constructor
25759  * Create a new HtmlEditor
25760  * @param {Object} config The config object
25761  */
25762
25763 Roo.bootstrap.HtmlEditor = function(config){
25764     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25765     if (!this.toolbars) {
25766         this.toolbars = [];
25767     }
25768     
25769     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25770     this.addEvents({
25771             /**
25772              * @event initialize
25773              * Fires when the editor is fully initialized (including the iframe)
25774              * @param {HtmlEditor} this
25775              */
25776             initialize: true,
25777             /**
25778              * @event activate
25779              * Fires when the editor is first receives the focus. Any insertion must wait
25780              * until after this event.
25781              * @param {HtmlEditor} this
25782              */
25783             activate: true,
25784              /**
25785              * @event beforesync
25786              * Fires before the textarea is updated with content from the editor iframe. Return false
25787              * to cancel the sync.
25788              * @param {HtmlEditor} this
25789              * @param {String} html
25790              */
25791             beforesync: true,
25792              /**
25793              * @event beforepush
25794              * Fires before the iframe editor is updated with content from the textarea. Return false
25795              * to cancel the push.
25796              * @param {HtmlEditor} this
25797              * @param {String} html
25798              */
25799             beforepush: true,
25800              /**
25801              * @event sync
25802              * Fires when the textarea is updated with content from the editor iframe.
25803              * @param {HtmlEditor} this
25804              * @param {String} html
25805              */
25806             sync: true,
25807              /**
25808              * @event push
25809              * Fires when the iframe editor is updated with content from the textarea.
25810              * @param {HtmlEditor} this
25811              * @param {String} html
25812              */
25813             push: true,
25814              /**
25815              * @event editmodechange
25816              * Fires when the editor switches edit modes
25817              * @param {HtmlEditor} this
25818              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25819              */
25820             editmodechange: true,
25821             /**
25822              * @event editorevent
25823              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25824              * @param {HtmlEditor} this
25825              */
25826             editorevent: true,
25827             /**
25828              * @event firstfocus
25829              * Fires when on first focus - needed by toolbars..
25830              * @param {HtmlEditor} this
25831              */
25832             firstfocus: true,
25833             /**
25834              * @event autosave
25835              * Auto save the htmlEditor value as a file into Events
25836              * @param {HtmlEditor} this
25837              */
25838             autosave: true,
25839             /**
25840              * @event savedpreview
25841              * preview the saved version of htmlEditor
25842              * @param {HtmlEditor} this
25843              */
25844             savedpreview: true
25845         });
25846 };
25847
25848
25849 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25850     
25851     
25852       /**
25853      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25854      */
25855     toolbars : false,
25856     
25857      /**
25858     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25859     */
25860     btns : [],
25861    
25862      /**
25863      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25864      *                        Roo.resizable.
25865      */
25866     resizable : false,
25867      /**
25868      * @cfg {Number} height (in pixels)
25869      */   
25870     height: 300,
25871    /**
25872      * @cfg {Number} width (in pixels)
25873      */   
25874     width: false,
25875     
25876     /**
25877      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25878      * 
25879      */
25880     stylesheets: false,
25881     
25882     // id of frame..
25883     frameId: false,
25884     
25885     // private properties
25886     validationEvent : false,
25887     deferHeight: true,
25888     initialized : false,
25889     activated : false,
25890     
25891     onFocus : Roo.emptyFn,
25892     iframePad:3,
25893     hideMode:'offsets',
25894     
25895     tbContainer : false,
25896     
25897     bodyCls : '',
25898     
25899     toolbarContainer :function() {
25900         return this.wrap.select('.x-html-editor-tb',true).first();
25901     },
25902
25903     /**
25904      * Protected method that will not generally be called directly. It
25905      * is called when the editor creates its toolbar. Override this method if you need to
25906      * add custom toolbar buttons.
25907      * @param {HtmlEditor} editor
25908      */
25909     createToolbar : function(){
25910         Roo.log('renewing');
25911         Roo.log("create toolbars");
25912         
25913         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25914         this.toolbars[0].render(this.toolbarContainer());
25915         
25916         return;
25917         
25918 //        if (!editor.toolbars || !editor.toolbars.length) {
25919 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25920 //        }
25921 //        
25922 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25923 //            editor.toolbars[i] = Roo.factory(
25924 //                    typeof(editor.toolbars[i]) == 'string' ?
25925 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25926 //                Roo.bootstrap.HtmlEditor);
25927 //            editor.toolbars[i].init(editor);
25928 //        }
25929     },
25930
25931      
25932     // private
25933     onRender : function(ct, position)
25934     {
25935        // Roo.log("Call onRender: " + this.xtype);
25936         var _t = this;
25937         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25938       
25939         this.wrap = this.inputEl().wrap({
25940             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25941         });
25942         
25943         this.editorcore.onRender(ct, position);
25944          
25945         if (this.resizable) {
25946             this.resizeEl = new Roo.Resizable(this.wrap, {
25947                 pinned : true,
25948                 wrap: true,
25949                 dynamic : true,
25950                 minHeight : this.height,
25951                 height: this.height,
25952                 handles : this.resizable,
25953                 width: this.width,
25954                 listeners : {
25955                     resize : function(r, w, h) {
25956                         _t.onResize(w,h); // -something
25957                     }
25958                 }
25959             });
25960             
25961         }
25962         this.createToolbar(this);
25963        
25964         
25965         if(!this.width && this.resizable){
25966             this.setSize(this.wrap.getSize());
25967         }
25968         if (this.resizeEl) {
25969             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25970             // should trigger onReize..
25971         }
25972         
25973     },
25974
25975     // private
25976     onResize : function(w, h)
25977     {
25978         Roo.log('resize: ' +w + ',' + h );
25979         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25980         var ew = false;
25981         var eh = false;
25982         
25983         if(this.inputEl() ){
25984             if(typeof w == 'number'){
25985                 var aw = w - this.wrap.getFrameWidth('lr');
25986                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25987                 ew = aw;
25988             }
25989             if(typeof h == 'number'){
25990                  var tbh = -11;  // fixme it needs to tool bar size!
25991                 for (var i =0; i < this.toolbars.length;i++) {
25992                     // fixme - ask toolbars for heights?
25993                     tbh += this.toolbars[i].el.getHeight();
25994                     //if (this.toolbars[i].footer) {
25995                     //    tbh += this.toolbars[i].footer.el.getHeight();
25996                     //}
25997                 }
25998               
25999                 
26000                 
26001                 
26002                 
26003                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26004                 ah -= 5; // knock a few pixes off for look..
26005                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26006                 var eh = ah;
26007             }
26008         }
26009         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26010         this.editorcore.onResize(ew,eh);
26011         
26012     },
26013
26014     /**
26015      * Toggles the editor between standard and source edit mode.
26016      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26017      */
26018     toggleSourceEdit : function(sourceEditMode)
26019     {
26020         this.editorcore.toggleSourceEdit(sourceEditMode);
26021         
26022         if(this.editorcore.sourceEditMode){
26023             Roo.log('editor - showing textarea');
26024             
26025 //            Roo.log('in');
26026 //            Roo.log(this.syncValue());
26027             this.syncValue();
26028             this.inputEl().removeClass(['hide', 'x-hidden']);
26029             this.inputEl().dom.removeAttribute('tabIndex');
26030             this.inputEl().focus();
26031         }else{
26032             Roo.log('editor - hiding textarea');
26033 //            Roo.log('out')
26034 //            Roo.log(this.pushValue()); 
26035             this.pushValue();
26036             
26037             this.inputEl().addClass(['hide', 'x-hidden']);
26038             this.inputEl().dom.setAttribute('tabIndex', -1);
26039             //this.deferFocus();
26040         }
26041          
26042         if(this.resizable){
26043             this.setSize(this.wrap.getSize());
26044         }
26045         
26046         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26047     },
26048  
26049     // private (for BoxComponent)
26050     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26051
26052     // private (for BoxComponent)
26053     getResizeEl : function(){
26054         return this.wrap;
26055     },
26056
26057     // private (for BoxComponent)
26058     getPositionEl : function(){
26059         return this.wrap;
26060     },
26061
26062     // private
26063     initEvents : function(){
26064         this.originalValue = this.getValue();
26065     },
26066
26067 //    /**
26068 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26069 //     * @method
26070 //     */
26071 //    markInvalid : Roo.emptyFn,
26072 //    /**
26073 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26074 //     * @method
26075 //     */
26076 //    clearInvalid : Roo.emptyFn,
26077
26078     setValue : function(v){
26079         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26080         this.editorcore.pushValue();
26081     },
26082
26083      
26084     // private
26085     deferFocus : function(){
26086         this.focus.defer(10, this);
26087     },
26088
26089     // doc'ed in Field
26090     focus : function(){
26091         this.editorcore.focus();
26092         
26093     },
26094       
26095
26096     // private
26097     onDestroy : function(){
26098         
26099         
26100         
26101         if(this.rendered){
26102             
26103             for (var i =0; i < this.toolbars.length;i++) {
26104                 // fixme - ask toolbars for heights?
26105                 this.toolbars[i].onDestroy();
26106             }
26107             
26108             this.wrap.dom.innerHTML = '';
26109             this.wrap.remove();
26110         }
26111     },
26112
26113     // private
26114     onFirstFocus : function(){
26115         //Roo.log("onFirstFocus");
26116         this.editorcore.onFirstFocus();
26117          for (var i =0; i < this.toolbars.length;i++) {
26118             this.toolbars[i].onFirstFocus();
26119         }
26120         
26121     },
26122     
26123     // private
26124     syncValue : function()
26125     {   
26126         this.editorcore.syncValue();
26127     },
26128     
26129     pushValue : function()
26130     {   
26131         this.editorcore.pushValue();
26132     }
26133      
26134     
26135     // hide stuff that is not compatible
26136     /**
26137      * @event blur
26138      * @hide
26139      */
26140     /**
26141      * @event change
26142      * @hide
26143      */
26144     /**
26145      * @event focus
26146      * @hide
26147      */
26148     /**
26149      * @event specialkey
26150      * @hide
26151      */
26152     /**
26153      * @cfg {String} fieldClass @hide
26154      */
26155     /**
26156      * @cfg {String} focusClass @hide
26157      */
26158     /**
26159      * @cfg {String} autoCreate @hide
26160      */
26161     /**
26162      * @cfg {String} inputType @hide
26163      */
26164      
26165     /**
26166      * @cfg {String} invalidText @hide
26167      */
26168     /**
26169      * @cfg {String} msgFx @hide
26170      */
26171     /**
26172      * @cfg {String} validateOnBlur @hide
26173      */
26174 });
26175  
26176     
26177    
26178    
26179    
26180       
26181 Roo.namespace('Roo.bootstrap.htmleditor');
26182 /**
26183  * @class Roo.bootstrap.HtmlEditorToolbar1
26184  * Basic Toolbar
26185  * 
26186  * @example
26187  * Usage:
26188  *
26189  new Roo.bootstrap.HtmlEditor({
26190     ....
26191     toolbars : [
26192         new Roo.bootstrap.HtmlEditorToolbar1({
26193             disable : { fonts: 1 , format: 1, ..., ... , ...],
26194             btns : [ .... ]
26195         })
26196     }
26197      
26198  * 
26199  * @cfg {Object} disable List of elements to disable..
26200  * @cfg {Array} btns List of additional buttons.
26201  * 
26202  * 
26203  * NEEDS Extra CSS? 
26204  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26205  */
26206  
26207 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26208 {
26209     
26210     Roo.apply(this, config);
26211     
26212     // default disabled, based on 'good practice'..
26213     this.disable = this.disable || {};
26214     Roo.applyIf(this.disable, {
26215         fontSize : true,
26216         colors : true,
26217         specialElements : true
26218     });
26219     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26220     
26221     this.editor = config.editor;
26222     this.editorcore = config.editor.editorcore;
26223     
26224     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26225     
26226     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26227     // dont call parent... till later.
26228 }
26229 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26230      
26231     bar : true,
26232     
26233     editor : false,
26234     editorcore : false,
26235     
26236     
26237     formats : [
26238         "p" ,  
26239         "h1","h2","h3","h4","h5","h6", 
26240         "pre", "code", 
26241         "abbr", "acronym", "address", "cite", "samp", "var",
26242         'div','span'
26243     ],
26244     
26245     onRender : function(ct, position)
26246     {
26247        // Roo.log("Call onRender: " + this.xtype);
26248         
26249        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26250        Roo.log(this.el);
26251        this.el.dom.style.marginBottom = '0';
26252        var _this = this;
26253        var editorcore = this.editorcore;
26254        var editor= this.editor;
26255        
26256        var children = [];
26257        var btn = function(id,cmd , toggle, handler, html){
26258        
26259             var  event = toggle ? 'toggle' : 'click';
26260        
26261             var a = {
26262                 size : 'sm',
26263                 xtype: 'Button',
26264                 xns: Roo.bootstrap,
26265                 //glyphicon : id,
26266                 fa: id,
26267                 cmd : id || cmd,
26268                 enableToggle:toggle !== false,
26269                 html : html || '',
26270                 pressed : toggle ? false : null,
26271                 listeners : {}
26272             };
26273             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26274                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26275             };
26276             children.push(a);
26277             return a;
26278        }
26279        
26280     //    var cb_box = function...
26281         
26282         var style = {
26283                 xtype: 'Button',
26284                 size : 'sm',
26285                 xns: Roo.bootstrap,
26286                 fa : 'font',
26287                 //html : 'submit'
26288                 menu : {
26289                     xtype: 'Menu',
26290                     xns: Roo.bootstrap,
26291                     items:  []
26292                 }
26293         };
26294         Roo.each(this.formats, function(f) {
26295             style.menu.items.push({
26296                 xtype :'MenuItem',
26297                 xns: Roo.bootstrap,
26298                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26299                 tagname : f,
26300                 listeners : {
26301                     click : function()
26302                     {
26303                         editorcore.insertTag(this.tagname);
26304                         editor.focus();
26305                     }
26306                 }
26307                 
26308             });
26309         });
26310         children.push(style);   
26311         
26312         btn('bold',false,true);
26313         btn('italic',false,true);
26314         btn('align-left', 'justifyleft',true);
26315         btn('align-center', 'justifycenter',true);
26316         btn('align-right' , 'justifyright',true);
26317         btn('link', false, false, function(btn) {
26318             //Roo.log("create link?");
26319             var url = prompt(this.createLinkText, this.defaultLinkValue);
26320             if(url && url != 'http:/'+'/'){
26321                 this.editorcore.relayCmd('createlink', url);
26322             }
26323         }),
26324         btn('list','insertunorderedlist',true);
26325         btn('pencil', false,true, function(btn){
26326                 Roo.log(this);
26327                 this.toggleSourceEdit(btn.pressed);
26328         });
26329         
26330         if (this.editor.btns.length > 0) {
26331             for (var i = 0; i<this.editor.btns.length; i++) {
26332                 children.push(this.editor.btns[i]);
26333             }
26334         }
26335         
26336         /*
26337         var cog = {
26338                 xtype: 'Button',
26339                 size : 'sm',
26340                 xns: Roo.bootstrap,
26341                 glyphicon : 'cog',
26342                 //html : 'submit'
26343                 menu : {
26344                     xtype: 'Menu',
26345                     xns: Roo.bootstrap,
26346                     items:  []
26347                 }
26348         };
26349         
26350         cog.menu.items.push({
26351             xtype :'MenuItem',
26352             xns: Roo.bootstrap,
26353             html : Clean styles,
26354             tagname : f,
26355             listeners : {
26356                 click : function()
26357                 {
26358                     editorcore.insertTag(this.tagname);
26359                     editor.focus();
26360                 }
26361             }
26362             
26363         });
26364        */
26365         
26366          
26367        this.xtype = 'NavSimplebar';
26368         
26369         for(var i=0;i< children.length;i++) {
26370             
26371             this.buttons.add(this.addxtypeChild(children[i]));
26372             
26373         }
26374         
26375         editor.on('editorevent', this.updateToolbar, this);
26376     },
26377     onBtnClick : function(id)
26378     {
26379        this.editorcore.relayCmd(id);
26380        this.editorcore.focus();
26381     },
26382     
26383     /**
26384      * Protected method that will not generally be called directly. It triggers
26385      * a toolbar update by reading the markup state of the current selection in the editor.
26386      */
26387     updateToolbar: function(){
26388
26389         if(!this.editorcore.activated){
26390             this.editor.onFirstFocus(); // is this neeed?
26391             return;
26392         }
26393
26394         var btns = this.buttons; 
26395         var doc = this.editorcore.doc;
26396         btns.get('bold').setActive(doc.queryCommandState('bold'));
26397         btns.get('italic').setActive(doc.queryCommandState('italic'));
26398         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26399         
26400         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26401         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26402         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26403         
26404         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26405         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26406          /*
26407         
26408         var ans = this.editorcore.getAllAncestors();
26409         if (this.formatCombo) {
26410             
26411             
26412             var store = this.formatCombo.store;
26413             this.formatCombo.setValue("");
26414             for (var i =0; i < ans.length;i++) {
26415                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26416                     // select it..
26417                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26418                     break;
26419                 }
26420             }
26421         }
26422         
26423         
26424         
26425         // hides menus... - so this cant be on a menu...
26426         Roo.bootstrap.MenuMgr.hideAll();
26427         */
26428         Roo.bootstrap.MenuMgr.hideAll();
26429         //this.editorsyncValue();
26430     },
26431     onFirstFocus: function() {
26432         this.buttons.each(function(item){
26433            item.enable();
26434         });
26435     },
26436     toggleSourceEdit : function(sourceEditMode){
26437         
26438           
26439         if(sourceEditMode){
26440             Roo.log("disabling buttons");
26441            this.buttons.each( function(item){
26442                 if(item.cmd != 'pencil'){
26443                     item.disable();
26444                 }
26445             });
26446           
26447         }else{
26448             Roo.log("enabling buttons");
26449             if(this.editorcore.initialized){
26450                 this.buttons.each( function(item){
26451                     item.enable();
26452                 });
26453             }
26454             
26455         }
26456         Roo.log("calling toggole on editor");
26457         // tell the editor that it's been pressed..
26458         this.editor.toggleSourceEdit(sourceEditMode);
26459        
26460     }
26461 });
26462
26463
26464
26465
26466  
26467 /*
26468  * - LGPL
26469  */
26470
26471 /**
26472  * @class Roo.bootstrap.Markdown
26473  * @extends Roo.bootstrap.TextArea
26474  * Bootstrap Showdown editable area
26475  * @cfg {string} content
26476  * 
26477  * @constructor
26478  * Create a new Showdown
26479  */
26480
26481 Roo.bootstrap.Markdown = function(config){
26482     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26483    
26484 };
26485
26486 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26487     
26488     editing :false,
26489     
26490     initEvents : function()
26491     {
26492         
26493         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26494         this.markdownEl = this.el.createChild({
26495             cls : 'roo-markdown-area'
26496         });
26497         this.inputEl().addClass('d-none');
26498         if (this.getValue() == '') {
26499             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26500             
26501         } else {
26502             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26503         }
26504         this.markdownEl.on('click', this.toggleTextEdit, this);
26505         this.on('blur', this.toggleTextEdit, this);
26506         this.on('specialkey', this.resizeTextArea, this);
26507     },
26508     
26509     toggleTextEdit : function()
26510     {
26511         var sh = this.markdownEl.getHeight();
26512         this.inputEl().addClass('d-none');
26513         this.markdownEl.addClass('d-none');
26514         if (!this.editing) {
26515             // show editor?
26516             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26517             this.inputEl().removeClass('d-none');
26518             this.inputEl().focus();
26519             this.editing = true;
26520             return;
26521         }
26522         // show showdown...
26523         this.updateMarkdown();
26524         this.markdownEl.removeClass('d-none');
26525         this.editing = false;
26526         return;
26527     },
26528     updateMarkdown : function()
26529     {
26530         if (this.getValue() == '') {
26531             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26532             return;
26533         }
26534  
26535         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26536     },
26537     
26538     resizeTextArea: function () {
26539         
26540         var sh = 100;
26541         Roo.log([sh, this.getValue().split("\n").length * 30]);
26542         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26543     },
26544     setValue : function(val)
26545     {
26546         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26547         if (!this.editing) {
26548             this.updateMarkdown();
26549         }
26550         
26551     },
26552     focus : function()
26553     {
26554         if (!this.editing) {
26555             this.toggleTextEdit();
26556         }
26557         
26558     }
26559
26560
26561 });
26562 /**
26563  * @class Roo.bootstrap.Table.AbstractSelectionModel
26564  * @extends Roo.util.Observable
26565  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26566  * implemented by descendant classes.  This class should not be directly instantiated.
26567  * @constructor
26568  */
26569 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26570     this.locked = false;
26571     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26572 };
26573
26574
26575 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26576     /** @ignore Called by the grid automatically. Do not call directly. */
26577     init : function(grid){
26578         this.grid = grid;
26579         this.initEvents();
26580     },
26581
26582     /**
26583      * Locks the selections.
26584      */
26585     lock : function(){
26586         this.locked = true;
26587     },
26588
26589     /**
26590      * Unlocks the selections.
26591      */
26592     unlock : function(){
26593         this.locked = false;
26594     },
26595
26596     /**
26597      * Returns true if the selections are locked.
26598      * @return {Boolean}
26599      */
26600     isLocked : function(){
26601         return this.locked;
26602     },
26603     
26604     
26605     initEvents : function ()
26606     {
26607         
26608     }
26609 });
26610 /**
26611  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26612  * @class Roo.bootstrap.Table.RowSelectionModel
26613  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26614  * It supports multiple selections and keyboard selection/navigation. 
26615  * @constructor
26616  * @param {Object} config
26617  */
26618
26619 Roo.bootstrap.Table.RowSelectionModel = function(config){
26620     Roo.apply(this, config);
26621     this.selections = new Roo.util.MixedCollection(false, function(o){
26622         return o.id;
26623     });
26624
26625     this.last = false;
26626     this.lastActive = false;
26627
26628     this.addEvents({
26629         /**
26630              * @event selectionchange
26631              * Fires when the selection changes
26632              * @param {SelectionModel} this
26633              */
26634             "selectionchange" : true,
26635         /**
26636              * @event afterselectionchange
26637              * Fires after the selection changes (eg. by key press or clicking)
26638              * @param {SelectionModel} this
26639              */
26640             "afterselectionchange" : true,
26641         /**
26642              * @event beforerowselect
26643              * Fires when a row is selected being selected, return false to cancel.
26644              * @param {SelectionModel} this
26645              * @param {Number} rowIndex The selected index
26646              * @param {Boolean} keepExisting False if other selections will be cleared
26647              */
26648             "beforerowselect" : true,
26649         /**
26650              * @event rowselect
26651              * Fires when a row is selected.
26652              * @param {SelectionModel} this
26653              * @param {Number} rowIndex The selected index
26654              * @param {Roo.data.Record} r The record
26655              */
26656             "rowselect" : true,
26657         /**
26658              * @event rowdeselect
26659              * Fires when a row is deselected.
26660              * @param {SelectionModel} this
26661              * @param {Number} rowIndex The selected index
26662              */
26663         "rowdeselect" : true
26664     });
26665     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26666     this.locked = false;
26667  };
26668
26669 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26670     /**
26671      * @cfg {Boolean} singleSelect
26672      * True to allow selection of only one row at a time (defaults to false)
26673      */
26674     singleSelect : false,
26675
26676     // private
26677     initEvents : function()
26678     {
26679
26680         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26681         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26682         //}else{ // allow click to work like normal
26683          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26684         //}
26685         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26686         this.grid.on("rowclick", this.handleMouseDown, this);
26687         
26688         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26689             "up" : function(e){
26690                 if(!e.shiftKey){
26691                     this.selectPrevious(e.shiftKey);
26692                 }else if(this.last !== false && this.lastActive !== false){
26693                     var last = this.last;
26694                     this.selectRange(this.last,  this.lastActive-1);
26695                     this.grid.getView().focusRow(this.lastActive);
26696                     if(last !== false){
26697                         this.last = last;
26698                     }
26699                 }else{
26700                     this.selectFirstRow();
26701                 }
26702                 this.fireEvent("afterselectionchange", this);
26703             },
26704             "down" : function(e){
26705                 if(!e.shiftKey){
26706                     this.selectNext(e.shiftKey);
26707                 }else if(this.last !== false && this.lastActive !== false){
26708                     var last = this.last;
26709                     this.selectRange(this.last,  this.lastActive+1);
26710                     this.grid.getView().focusRow(this.lastActive);
26711                     if(last !== false){
26712                         this.last = last;
26713                     }
26714                 }else{
26715                     this.selectFirstRow();
26716                 }
26717                 this.fireEvent("afterselectionchange", this);
26718             },
26719             scope: this
26720         });
26721         this.grid.store.on('load', function(){
26722             this.selections.clear();
26723         },this);
26724         /*
26725         var view = this.grid.view;
26726         view.on("refresh", this.onRefresh, this);
26727         view.on("rowupdated", this.onRowUpdated, this);
26728         view.on("rowremoved", this.onRemove, this);
26729         */
26730     },
26731
26732     // private
26733     onRefresh : function()
26734     {
26735         var ds = this.grid.store, i, v = this.grid.view;
26736         var s = this.selections;
26737         s.each(function(r){
26738             if((i = ds.indexOfId(r.id)) != -1){
26739                 v.onRowSelect(i);
26740             }else{
26741                 s.remove(r);
26742             }
26743         });
26744     },
26745
26746     // private
26747     onRemove : function(v, index, r){
26748         this.selections.remove(r);
26749     },
26750
26751     // private
26752     onRowUpdated : function(v, index, r){
26753         if(this.isSelected(r)){
26754             v.onRowSelect(index);
26755         }
26756     },
26757
26758     /**
26759      * Select records.
26760      * @param {Array} records The records to select
26761      * @param {Boolean} keepExisting (optional) True to keep existing selections
26762      */
26763     selectRecords : function(records, keepExisting)
26764     {
26765         if(!keepExisting){
26766             this.clearSelections();
26767         }
26768             var ds = this.grid.store;
26769         for(var i = 0, len = records.length; i < len; i++){
26770             this.selectRow(ds.indexOf(records[i]), true);
26771         }
26772     },
26773
26774     /**
26775      * Gets the number of selected rows.
26776      * @return {Number}
26777      */
26778     getCount : function(){
26779         return this.selections.length;
26780     },
26781
26782     /**
26783      * Selects the first row in the grid.
26784      */
26785     selectFirstRow : function(){
26786         this.selectRow(0);
26787     },
26788
26789     /**
26790      * Select the last row.
26791      * @param {Boolean} keepExisting (optional) True to keep existing selections
26792      */
26793     selectLastRow : function(keepExisting){
26794         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26795         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26796     },
26797
26798     /**
26799      * Selects the row immediately following the last selected row.
26800      * @param {Boolean} keepExisting (optional) True to keep existing selections
26801      */
26802     selectNext : function(keepExisting)
26803     {
26804             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26805             this.selectRow(this.last+1, keepExisting);
26806             this.grid.getView().focusRow(this.last);
26807         }
26808     },
26809
26810     /**
26811      * Selects the row that precedes the last selected row.
26812      * @param {Boolean} keepExisting (optional) True to keep existing selections
26813      */
26814     selectPrevious : function(keepExisting){
26815         if(this.last){
26816             this.selectRow(this.last-1, keepExisting);
26817             this.grid.getView().focusRow(this.last);
26818         }
26819     },
26820
26821     /**
26822      * Returns the selected records
26823      * @return {Array} Array of selected records
26824      */
26825     getSelections : function(){
26826         return [].concat(this.selections.items);
26827     },
26828
26829     /**
26830      * Returns the first selected record.
26831      * @return {Record}
26832      */
26833     getSelected : function(){
26834         return this.selections.itemAt(0);
26835     },
26836
26837
26838     /**
26839      * Clears all selections.
26840      */
26841     clearSelections : function(fast)
26842     {
26843         if(this.locked) {
26844             return;
26845         }
26846         if(fast !== true){
26847                 var ds = this.grid.store;
26848             var s = this.selections;
26849             s.each(function(r){
26850                 this.deselectRow(ds.indexOfId(r.id));
26851             }, this);
26852             s.clear();
26853         }else{
26854             this.selections.clear();
26855         }
26856         this.last = false;
26857     },
26858
26859
26860     /**
26861      * Selects all rows.
26862      */
26863     selectAll : function(){
26864         if(this.locked) {
26865             return;
26866         }
26867         this.selections.clear();
26868         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26869             this.selectRow(i, true);
26870         }
26871     },
26872
26873     /**
26874      * Returns True if there is a selection.
26875      * @return {Boolean}
26876      */
26877     hasSelection : function(){
26878         return this.selections.length > 0;
26879     },
26880
26881     /**
26882      * Returns True if the specified row is selected.
26883      * @param {Number/Record} record The record or index of the record to check
26884      * @return {Boolean}
26885      */
26886     isSelected : function(index){
26887             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26888         return (r && this.selections.key(r.id) ? true : false);
26889     },
26890
26891     /**
26892      * Returns True if the specified record id is selected.
26893      * @param {String} id The id of record to check
26894      * @return {Boolean}
26895      */
26896     isIdSelected : function(id){
26897         return (this.selections.key(id) ? true : false);
26898     },
26899
26900
26901     // private
26902     handleMouseDBClick : function(e, t){
26903         
26904     },
26905     // private
26906     handleMouseDown : function(e, t)
26907     {
26908             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26909         if(this.isLocked() || rowIndex < 0 ){
26910             return;
26911         };
26912         if(e.shiftKey && this.last !== false){
26913             var last = this.last;
26914             this.selectRange(last, rowIndex, e.ctrlKey);
26915             this.last = last; // reset the last
26916             t.focus();
26917     
26918         }else{
26919             var isSelected = this.isSelected(rowIndex);
26920             //Roo.log("select row:" + rowIndex);
26921             if(isSelected){
26922                 this.deselectRow(rowIndex);
26923             } else {
26924                         this.selectRow(rowIndex, true);
26925             }
26926     
26927             /*
26928                 if(e.button !== 0 && isSelected){
26929                 alert('rowIndex 2: ' + rowIndex);
26930                     view.focusRow(rowIndex);
26931                 }else if(e.ctrlKey && isSelected){
26932                     this.deselectRow(rowIndex);
26933                 }else if(!isSelected){
26934                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26935                     view.focusRow(rowIndex);
26936                 }
26937             */
26938         }
26939         this.fireEvent("afterselectionchange", this);
26940     },
26941     // private
26942     handleDragableRowClick :  function(grid, rowIndex, e) 
26943     {
26944         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26945             this.selectRow(rowIndex, false);
26946             grid.view.focusRow(rowIndex);
26947              this.fireEvent("afterselectionchange", this);
26948         }
26949     },
26950     
26951     /**
26952      * Selects multiple rows.
26953      * @param {Array} rows Array of the indexes of the row to select
26954      * @param {Boolean} keepExisting (optional) True to keep existing selections
26955      */
26956     selectRows : function(rows, keepExisting){
26957         if(!keepExisting){
26958             this.clearSelections();
26959         }
26960         for(var i = 0, len = rows.length; i < len; i++){
26961             this.selectRow(rows[i], true);
26962         }
26963     },
26964
26965     /**
26966      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26967      * @param {Number} startRow The index of the first row in the range
26968      * @param {Number} endRow The index of the last row in the range
26969      * @param {Boolean} keepExisting (optional) True to retain existing selections
26970      */
26971     selectRange : function(startRow, endRow, keepExisting){
26972         if(this.locked) {
26973             return;
26974         }
26975         if(!keepExisting){
26976             this.clearSelections();
26977         }
26978         if(startRow <= endRow){
26979             for(var i = startRow; i <= endRow; i++){
26980                 this.selectRow(i, true);
26981             }
26982         }else{
26983             for(var i = startRow; i >= endRow; i--){
26984                 this.selectRow(i, true);
26985             }
26986         }
26987     },
26988
26989     /**
26990      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26991      * @param {Number} startRow The index of the first row in the range
26992      * @param {Number} endRow The index of the last row in the range
26993      */
26994     deselectRange : function(startRow, endRow, preventViewNotify){
26995         if(this.locked) {
26996             return;
26997         }
26998         for(var i = startRow; i <= endRow; i++){
26999             this.deselectRow(i, preventViewNotify);
27000         }
27001     },
27002
27003     /**
27004      * Selects a row.
27005      * @param {Number} row The index of the row to select
27006      * @param {Boolean} keepExisting (optional) True to keep existing selections
27007      */
27008     selectRow : function(index, keepExisting, preventViewNotify)
27009     {
27010             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27011             return;
27012         }
27013         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27014             if(!keepExisting || this.singleSelect){
27015                 this.clearSelections();
27016             }
27017             
27018             var r = this.grid.store.getAt(index);
27019             //console.log('selectRow - record id :' + r.id);
27020             
27021             this.selections.add(r);
27022             this.last = this.lastActive = index;
27023             if(!preventViewNotify){
27024                 var proxy = new Roo.Element(
27025                                 this.grid.getRowDom(index)
27026                 );
27027                 proxy.addClass('bg-info info');
27028             }
27029             this.fireEvent("rowselect", this, index, r);
27030             this.fireEvent("selectionchange", this);
27031         }
27032     },
27033
27034     /**
27035      * Deselects a row.
27036      * @param {Number} row The index of the row to deselect
27037      */
27038     deselectRow : function(index, preventViewNotify)
27039     {
27040         if(this.locked) {
27041             return;
27042         }
27043         if(this.last == index){
27044             this.last = false;
27045         }
27046         if(this.lastActive == index){
27047             this.lastActive = false;
27048         }
27049         
27050         var r = this.grid.store.getAt(index);
27051         if (!r) {
27052             return;
27053         }
27054         
27055         this.selections.remove(r);
27056         //.console.log('deselectRow - record id :' + r.id);
27057         if(!preventViewNotify){
27058         
27059             var proxy = new Roo.Element(
27060                 this.grid.getRowDom(index)
27061             );
27062             proxy.removeClass('bg-info info');
27063         }
27064         this.fireEvent("rowdeselect", this, index);
27065         this.fireEvent("selectionchange", this);
27066     },
27067
27068     // private
27069     restoreLast : function(){
27070         if(this._last){
27071             this.last = this._last;
27072         }
27073     },
27074
27075     // private
27076     acceptsNav : function(row, col, cm){
27077         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27078     },
27079
27080     // private
27081     onEditorKey : function(field, e){
27082         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27083         if(k == e.TAB){
27084             e.stopEvent();
27085             ed.completeEdit();
27086             if(e.shiftKey){
27087                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27088             }else{
27089                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27090             }
27091         }else if(k == e.ENTER && !e.ctrlKey){
27092             e.stopEvent();
27093             ed.completeEdit();
27094             if(e.shiftKey){
27095                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27096             }else{
27097                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27098             }
27099         }else if(k == e.ESC){
27100             ed.cancelEdit();
27101         }
27102         if(newCell){
27103             g.startEditing(newCell[0], newCell[1]);
27104         }
27105     }
27106 });
27107 /*
27108  * Based on:
27109  * Ext JS Library 1.1.1
27110  * Copyright(c) 2006-2007, Ext JS, LLC.
27111  *
27112  * Originally Released Under LGPL - original licence link has changed is not relivant.
27113  *
27114  * Fork - LGPL
27115  * <script type="text/javascript">
27116  */
27117  
27118 /**
27119  * @class Roo.bootstrap.PagingToolbar
27120  * @extends Roo.bootstrap.NavSimplebar
27121  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27122  * @constructor
27123  * Create a new PagingToolbar
27124  * @param {Object} config The config object
27125  * @param {Roo.data.Store} store
27126  */
27127 Roo.bootstrap.PagingToolbar = function(config)
27128 {
27129     // old args format still supported... - xtype is prefered..
27130         // created from xtype...
27131     
27132     this.ds = config.dataSource;
27133     
27134     if (config.store && !this.ds) {
27135         this.store= Roo.factory(config.store, Roo.data);
27136         this.ds = this.store;
27137         this.ds.xmodule = this.xmodule || false;
27138     }
27139     
27140     this.toolbarItems = [];
27141     if (config.items) {
27142         this.toolbarItems = config.items;
27143     }
27144     
27145     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27146     
27147     this.cursor = 0;
27148     
27149     if (this.ds) { 
27150         this.bind(this.ds);
27151     }
27152     
27153     if (Roo.bootstrap.version == 4) {
27154         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27155     } else {
27156         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27157     }
27158     
27159 };
27160
27161 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27162     /**
27163      * @cfg {Roo.data.Store} dataSource
27164      * The underlying data store providing the paged data
27165      */
27166     /**
27167      * @cfg {String/HTMLElement/Element} container
27168      * container The id or element that will contain the toolbar
27169      */
27170     /**
27171      * @cfg {Boolean} displayInfo
27172      * True to display the displayMsg (defaults to false)
27173      */
27174     /**
27175      * @cfg {Number} pageSize
27176      * The number of records to display per page (defaults to 20)
27177      */
27178     pageSize: 20,
27179     /**
27180      * @cfg {String} displayMsg
27181      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27182      */
27183     displayMsg : 'Displaying {0} - {1} of {2}',
27184     /**
27185      * @cfg {String} emptyMsg
27186      * The message to display when no records are found (defaults to "No data to display")
27187      */
27188     emptyMsg : 'No data to display',
27189     /**
27190      * Customizable piece of the default paging text (defaults to "Page")
27191      * @type String
27192      */
27193     beforePageText : "Page",
27194     /**
27195      * Customizable piece of the default paging text (defaults to "of %0")
27196      * @type String
27197      */
27198     afterPageText : "of {0}",
27199     /**
27200      * Customizable piece of the default paging text (defaults to "First Page")
27201      * @type String
27202      */
27203     firstText : "First Page",
27204     /**
27205      * Customizable piece of the default paging text (defaults to "Previous Page")
27206      * @type String
27207      */
27208     prevText : "Previous Page",
27209     /**
27210      * Customizable piece of the default paging text (defaults to "Next Page")
27211      * @type String
27212      */
27213     nextText : "Next Page",
27214     /**
27215      * Customizable piece of the default paging text (defaults to "Last Page")
27216      * @type String
27217      */
27218     lastText : "Last Page",
27219     /**
27220      * Customizable piece of the default paging text (defaults to "Refresh")
27221      * @type String
27222      */
27223     refreshText : "Refresh",
27224
27225     buttons : false,
27226     // private
27227     onRender : function(ct, position) 
27228     {
27229         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27230         this.navgroup.parentId = this.id;
27231         this.navgroup.onRender(this.el, null);
27232         // add the buttons to the navgroup
27233         
27234         if(this.displayInfo){
27235             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27236             this.displayEl = this.el.select('.x-paging-info', true).first();
27237 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27238 //            this.displayEl = navel.el.select('span',true).first();
27239         }
27240         
27241         var _this = this;
27242         
27243         if(this.buttons){
27244             Roo.each(_this.buttons, function(e){ // this might need to use render????
27245                Roo.factory(e).render(_this.el);
27246             });
27247         }
27248             
27249         Roo.each(_this.toolbarItems, function(e) {
27250             _this.navgroup.addItem(e);
27251         });
27252         
27253         
27254         this.first = this.navgroup.addItem({
27255             tooltip: this.firstText,
27256             cls: "prev btn-outline-secondary",
27257             html : ' <i class="fa fa-step-backward"></i>',
27258             disabled: true,
27259             preventDefault: true,
27260             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27261         });
27262         
27263         this.prev =  this.navgroup.addItem({
27264             tooltip: this.prevText,
27265             cls: "prev btn-outline-secondary",
27266             html : ' <i class="fa fa-backward"></i>',
27267             disabled: true,
27268             preventDefault: true,
27269             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27270         });
27271     //this.addSeparator();
27272         
27273         
27274         var field = this.navgroup.addItem( {
27275             tagtype : 'span',
27276             cls : 'x-paging-position  btn-outline-secondary',
27277              disabled: true,
27278             html : this.beforePageText  +
27279                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27280                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27281          } ); //?? escaped?
27282         
27283         this.field = field.el.select('input', true).first();
27284         this.field.on("keydown", this.onPagingKeydown, this);
27285         this.field.on("focus", function(){this.dom.select();});
27286     
27287     
27288         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27289         //this.field.setHeight(18);
27290         //this.addSeparator();
27291         this.next = this.navgroup.addItem({
27292             tooltip: this.nextText,
27293             cls: "next btn-outline-secondary",
27294             html : ' <i class="fa fa-forward"></i>',
27295             disabled: true,
27296             preventDefault: true,
27297             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27298         });
27299         this.last = this.navgroup.addItem({
27300             tooltip: this.lastText,
27301             html : ' <i class="fa fa-step-forward"></i>',
27302             cls: "next btn-outline-secondary",
27303             disabled: true,
27304             preventDefault: true,
27305             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27306         });
27307     //this.addSeparator();
27308         this.loading = this.navgroup.addItem({
27309             tooltip: this.refreshText,
27310             cls: "btn-outline-secondary",
27311             html : ' <i class="fa fa-refresh"></i>',
27312             preventDefault: true,
27313             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27314         });
27315         
27316     },
27317
27318     // private
27319     updateInfo : function(){
27320         if(this.displayEl){
27321             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27322             var msg = count == 0 ?
27323                 this.emptyMsg :
27324                 String.format(
27325                     this.displayMsg,
27326                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27327                 );
27328             this.displayEl.update(msg);
27329         }
27330     },
27331
27332     // private
27333     onLoad : function(ds, r, o)
27334     {
27335         this.cursor = o.params && o.params.start ? o.params.start : 0;
27336         
27337         var d = this.getPageData(),
27338             ap = d.activePage,
27339             ps = d.pages;
27340         
27341         
27342         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27343         this.field.dom.value = ap;
27344         this.first.setDisabled(ap == 1);
27345         this.prev.setDisabled(ap == 1);
27346         this.next.setDisabled(ap == ps);
27347         this.last.setDisabled(ap == ps);
27348         this.loading.enable();
27349         this.updateInfo();
27350     },
27351
27352     // private
27353     getPageData : function(){
27354         var total = this.ds.getTotalCount();
27355         return {
27356             total : total,
27357             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27358             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27359         };
27360     },
27361
27362     // private
27363     onLoadError : function(){
27364         this.loading.enable();
27365     },
27366
27367     // private
27368     onPagingKeydown : function(e){
27369         var k = e.getKey();
27370         var d = this.getPageData();
27371         if(k == e.RETURN){
27372             var v = this.field.dom.value, pageNum;
27373             if(!v || isNaN(pageNum = parseInt(v, 10))){
27374                 this.field.dom.value = d.activePage;
27375                 return;
27376             }
27377             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27378             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27379             e.stopEvent();
27380         }
27381         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))
27382         {
27383           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27384           this.field.dom.value = pageNum;
27385           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27386           e.stopEvent();
27387         }
27388         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27389         {
27390           var v = this.field.dom.value, pageNum; 
27391           var increment = (e.shiftKey) ? 10 : 1;
27392           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27393                 increment *= -1;
27394           }
27395           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27396             this.field.dom.value = d.activePage;
27397             return;
27398           }
27399           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27400           {
27401             this.field.dom.value = parseInt(v, 10) + increment;
27402             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27403             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27404           }
27405           e.stopEvent();
27406         }
27407     },
27408
27409     // private
27410     beforeLoad : function(){
27411         if(this.loading){
27412             this.loading.disable();
27413         }
27414     },
27415
27416     // private
27417     onClick : function(which){
27418         
27419         var ds = this.ds;
27420         if (!ds) {
27421             return;
27422         }
27423         
27424         switch(which){
27425             case "first":
27426                 ds.load({params:{start: 0, limit: this.pageSize}});
27427             break;
27428             case "prev":
27429                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27430             break;
27431             case "next":
27432                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27433             break;
27434             case "last":
27435                 var total = ds.getTotalCount();
27436                 var extra = total % this.pageSize;
27437                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27438                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27439             break;
27440             case "refresh":
27441                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27442             break;
27443         }
27444     },
27445
27446     /**
27447      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27448      * @param {Roo.data.Store} store The data store to unbind
27449      */
27450     unbind : function(ds){
27451         ds.un("beforeload", this.beforeLoad, this);
27452         ds.un("load", this.onLoad, this);
27453         ds.un("loadexception", this.onLoadError, this);
27454         ds.un("remove", this.updateInfo, this);
27455         ds.un("add", this.updateInfo, this);
27456         this.ds = undefined;
27457     },
27458
27459     /**
27460      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27461      * @param {Roo.data.Store} store The data store to bind
27462      */
27463     bind : function(ds){
27464         ds.on("beforeload", this.beforeLoad, this);
27465         ds.on("load", this.onLoad, this);
27466         ds.on("loadexception", this.onLoadError, this);
27467         ds.on("remove", this.updateInfo, this);
27468         ds.on("add", this.updateInfo, this);
27469         this.ds = ds;
27470     }
27471 });/*
27472  * - LGPL
27473  *
27474  * element
27475  * 
27476  */
27477
27478 /**
27479  * @class Roo.bootstrap.MessageBar
27480  * @extends Roo.bootstrap.Component
27481  * Bootstrap MessageBar class
27482  * @cfg {String} html contents of the MessageBar
27483  * @cfg {String} weight (info | success | warning | danger) default info
27484  * @cfg {String} beforeClass insert the bar before the given class
27485  * @cfg {Boolean} closable (true | false) default false
27486  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27487  * 
27488  * @constructor
27489  * Create a new Element
27490  * @param {Object} config The config object
27491  */
27492
27493 Roo.bootstrap.MessageBar = function(config){
27494     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27495 };
27496
27497 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27498     
27499     html: '',
27500     weight: 'info',
27501     closable: false,
27502     fixed: false,
27503     beforeClass: 'bootstrap-sticky-wrap',
27504     
27505     getAutoCreate : function(){
27506         
27507         var cfg = {
27508             tag: 'div',
27509             cls: 'alert alert-dismissable alert-' + this.weight,
27510             cn: [
27511                 {
27512                     tag: 'span',
27513                     cls: 'message',
27514                     html: this.html || ''
27515                 }
27516             ]
27517         };
27518         
27519         if(this.fixed){
27520             cfg.cls += ' alert-messages-fixed';
27521         }
27522         
27523         if(this.closable){
27524             cfg.cn.push({
27525                 tag: 'button',
27526                 cls: 'close',
27527                 html: 'x'
27528             });
27529         }
27530         
27531         return cfg;
27532     },
27533     
27534     onRender : function(ct, position)
27535     {
27536         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27537         
27538         if(!this.el){
27539             var cfg = Roo.apply({},  this.getAutoCreate());
27540             cfg.id = Roo.id();
27541             
27542             if (this.cls) {
27543                 cfg.cls += ' ' + this.cls;
27544             }
27545             if (this.style) {
27546                 cfg.style = this.style;
27547             }
27548             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27549             
27550             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27551         }
27552         
27553         this.el.select('>button.close').on('click', this.hide, this);
27554         
27555     },
27556     
27557     show : function()
27558     {
27559         if (!this.rendered) {
27560             this.render();
27561         }
27562         
27563         this.el.show();
27564         
27565         this.fireEvent('show', this);
27566         
27567     },
27568     
27569     hide : function()
27570     {
27571         if (!this.rendered) {
27572             this.render();
27573         }
27574         
27575         this.el.hide();
27576         
27577         this.fireEvent('hide', this);
27578     },
27579     
27580     update : function()
27581     {
27582 //        var e = this.el.dom.firstChild;
27583 //        
27584 //        if(this.closable){
27585 //            e = e.nextSibling;
27586 //        }
27587 //        
27588 //        e.data = this.html || '';
27589
27590         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27591     }
27592    
27593 });
27594
27595  
27596
27597      /*
27598  * - LGPL
27599  *
27600  * Graph
27601  * 
27602  */
27603
27604
27605 /**
27606  * @class Roo.bootstrap.Graph
27607  * @extends Roo.bootstrap.Component
27608  * Bootstrap Graph class
27609 > Prameters
27610  -sm {number} sm 4
27611  -md {number} md 5
27612  @cfg {String} graphtype  bar | vbar | pie
27613  @cfg {number} g_x coodinator | centre x (pie)
27614  @cfg {number} g_y coodinator | centre y (pie)
27615  @cfg {number} g_r radius (pie)
27616  @cfg {number} g_height height of the chart (respected by all elements in the set)
27617  @cfg {number} g_width width of the chart (respected by all elements in the set)
27618  @cfg {Object} title The title of the chart
27619     
27620  -{Array}  values
27621  -opts (object) options for the chart 
27622      o {
27623      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27624      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27625      o vgutter (number)
27626      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.
27627      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27628      o to
27629      o stretch (boolean)
27630      o }
27631  -opts (object) options for the pie
27632      o{
27633      o cut
27634      o startAngle (number)
27635      o endAngle (number)
27636      } 
27637  *
27638  * @constructor
27639  * Create a new Input
27640  * @param {Object} config The config object
27641  */
27642
27643 Roo.bootstrap.Graph = function(config){
27644     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27645     
27646     this.addEvents({
27647         // img events
27648         /**
27649          * @event click
27650          * The img click event for the img.
27651          * @param {Roo.EventObject} e
27652          */
27653         "click" : true
27654     });
27655 };
27656
27657 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27658     
27659     sm: 4,
27660     md: 5,
27661     graphtype: 'bar',
27662     g_height: 250,
27663     g_width: 400,
27664     g_x: 50,
27665     g_y: 50,
27666     g_r: 30,
27667     opts:{
27668         //g_colors: this.colors,
27669         g_type: 'soft',
27670         g_gutter: '20%'
27671
27672     },
27673     title : false,
27674
27675     getAutoCreate : function(){
27676         
27677         var cfg = {
27678             tag: 'div',
27679             html : null
27680         };
27681         
27682         
27683         return  cfg;
27684     },
27685
27686     onRender : function(ct,position){
27687         
27688         
27689         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27690         
27691         if (typeof(Raphael) == 'undefined') {
27692             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27693             return;
27694         }
27695         
27696         this.raphael = Raphael(this.el.dom);
27697         
27698                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27699                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27700                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27701                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27702                 /*
27703                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27704                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27705                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27706                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27707                 
27708                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27709                 r.barchart(330, 10, 300, 220, data1);
27710                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27711                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27712                 */
27713                 
27714                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27715                 // r.barchart(30, 30, 560, 250,  xdata, {
27716                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27717                 //     axis : "0 0 1 1",
27718                 //     axisxlabels :  xdata
27719                 //     //yvalues : cols,
27720                    
27721                 // });
27722 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27723 //        
27724 //        this.load(null,xdata,{
27725 //                axis : "0 0 1 1",
27726 //                axisxlabels :  xdata
27727 //                });
27728
27729     },
27730
27731     load : function(graphtype,xdata,opts)
27732     {
27733         this.raphael.clear();
27734         if(!graphtype) {
27735             graphtype = this.graphtype;
27736         }
27737         if(!opts){
27738             opts = this.opts;
27739         }
27740         var r = this.raphael,
27741             fin = function () {
27742                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27743             },
27744             fout = function () {
27745                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27746             },
27747             pfin = function() {
27748                 this.sector.stop();
27749                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27750
27751                 if (this.label) {
27752                     this.label[0].stop();
27753                     this.label[0].attr({ r: 7.5 });
27754                     this.label[1].attr({ "font-weight": 800 });
27755                 }
27756             },
27757             pfout = function() {
27758                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27759
27760                 if (this.label) {
27761                     this.label[0].animate({ r: 5 }, 500, "bounce");
27762                     this.label[1].attr({ "font-weight": 400 });
27763                 }
27764             };
27765
27766         switch(graphtype){
27767             case 'bar':
27768                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27769                 break;
27770             case 'hbar':
27771                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27772                 break;
27773             case 'pie':
27774 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27775 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27776 //            
27777                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27778                 
27779                 break;
27780
27781         }
27782         
27783         if(this.title){
27784             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27785         }
27786         
27787     },
27788     
27789     setTitle: function(o)
27790     {
27791         this.title = o;
27792     },
27793     
27794     initEvents: function() {
27795         
27796         if(!this.href){
27797             this.el.on('click', this.onClick, this);
27798         }
27799     },
27800     
27801     onClick : function(e)
27802     {
27803         Roo.log('img onclick');
27804         this.fireEvent('click', this, e);
27805     }
27806    
27807 });
27808
27809  
27810 /*
27811  * - LGPL
27812  *
27813  * numberBox
27814  * 
27815  */
27816 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27817
27818 /**
27819  * @class Roo.bootstrap.dash.NumberBox
27820  * @extends Roo.bootstrap.Component
27821  * Bootstrap NumberBox class
27822  * @cfg {String} headline Box headline
27823  * @cfg {String} content Box content
27824  * @cfg {String} icon Box icon
27825  * @cfg {String} footer Footer text
27826  * @cfg {String} fhref Footer href
27827  * 
27828  * @constructor
27829  * Create a new NumberBox
27830  * @param {Object} config The config object
27831  */
27832
27833
27834 Roo.bootstrap.dash.NumberBox = function(config){
27835     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27836     
27837 };
27838
27839 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27840     
27841     headline : '',
27842     content : '',
27843     icon : '',
27844     footer : '',
27845     fhref : '',
27846     ficon : '',
27847     
27848     getAutoCreate : function(){
27849         
27850         var cfg = {
27851             tag : 'div',
27852             cls : 'small-box ',
27853             cn : [
27854                 {
27855                     tag : 'div',
27856                     cls : 'inner',
27857                     cn :[
27858                         {
27859                             tag : 'h3',
27860                             cls : 'roo-headline',
27861                             html : this.headline
27862                         },
27863                         {
27864                             tag : 'p',
27865                             cls : 'roo-content',
27866                             html : this.content
27867                         }
27868                     ]
27869                 }
27870             ]
27871         };
27872         
27873         if(this.icon){
27874             cfg.cn.push({
27875                 tag : 'div',
27876                 cls : 'icon',
27877                 cn :[
27878                     {
27879                         tag : 'i',
27880                         cls : 'ion ' + this.icon
27881                     }
27882                 ]
27883             });
27884         }
27885         
27886         if(this.footer){
27887             var footer = {
27888                 tag : 'a',
27889                 cls : 'small-box-footer',
27890                 href : this.fhref || '#',
27891                 html : this.footer
27892             };
27893             
27894             cfg.cn.push(footer);
27895             
27896         }
27897         
27898         return  cfg;
27899     },
27900
27901     onRender : function(ct,position){
27902         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27903
27904
27905        
27906                 
27907     },
27908
27909     setHeadline: function (value)
27910     {
27911         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27912     },
27913     
27914     setFooter: function (value, href)
27915     {
27916         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27917         
27918         if(href){
27919             this.el.select('a.small-box-footer',true).first().attr('href', href);
27920         }
27921         
27922     },
27923
27924     setContent: function (value)
27925     {
27926         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27927     },
27928
27929     initEvents: function() 
27930     {   
27931         
27932     }
27933     
27934 });
27935
27936  
27937 /*
27938  * - LGPL
27939  *
27940  * TabBox
27941  * 
27942  */
27943 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27944
27945 /**
27946  * @class Roo.bootstrap.dash.TabBox
27947  * @extends Roo.bootstrap.Component
27948  * Bootstrap TabBox class
27949  * @cfg {String} title Title of the TabBox
27950  * @cfg {String} icon Icon of the TabBox
27951  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27952  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27953  * 
27954  * @constructor
27955  * Create a new TabBox
27956  * @param {Object} config The config object
27957  */
27958
27959
27960 Roo.bootstrap.dash.TabBox = function(config){
27961     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27962     this.addEvents({
27963         // raw events
27964         /**
27965          * @event addpane
27966          * When a pane is added
27967          * @param {Roo.bootstrap.dash.TabPane} pane
27968          */
27969         "addpane" : true,
27970         /**
27971          * @event activatepane
27972          * When a pane is activated
27973          * @param {Roo.bootstrap.dash.TabPane} pane
27974          */
27975         "activatepane" : true
27976         
27977          
27978     });
27979     
27980     this.panes = [];
27981 };
27982
27983 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27984
27985     title : '',
27986     icon : false,
27987     showtabs : true,
27988     tabScrollable : false,
27989     
27990     getChildContainer : function()
27991     {
27992         return this.el.select('.tab-content', true).first();
27993     },
27994     
27995     getAutoCreate : function(){
27996         
27997         var header = {
27998             tag: 'li',
27999             cls: 'pull-left header',
28000             html: this.title,
28001             cn : []
28002         };
28003         
28004         if(this.icon){
28005             header.cn.push({
28006                 tag: 'i',
28007                 cls: 'fa ' + this.icon
28008             });
28009         }
28010         
28011         var h = {
28012             tag: 'ul',
28013             cls: 'nav nav-tabs pull-right',
28014             cn: [
28015                 header
28016             ]
28017         };
28018         
28019         if(this.tabScrollable){
28020             h = {
28021                 tag: 'div',
28022                 cls: 'tab-header',
28023                 cn: [
28024                     {
28025                         tag: 'ul',
28026                         cls: 'nav nav-tabs pull-right',
28027                         cn: [
28028                             header
28029                         ]
28030                     }
28031                 ]
28032             };
28033         }
28034         
28035         var cfg = {
28036             tag: 'div',
28037             cls: 'nav-tabs-custom',
28038             cn: [
28039                 h,
28040                 {
28041                     tag: 'div',
28042                     cls: 'tab-content no-padding',
28043                     cn: []
28044                 }
28045             ]
28046         };
28047
28048         return  cfg;
28049     },
28050     initEvents : function()
28051     {
28052         //Roo.log('add add pane handler');
28053         this.on('addpane', this.onAddPane, this);
28054     },
28055      /**
28056      * Updates the box title
28057      * @param {String} html to set the title to.
28058      */
28059     setTitle : function(value)
28060     {
28061         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28062     },
28063     onAddPane : function(pane)
28064     {
28065         this.panes.push(pane);
28066         //Roo.log('addpane');
28067         //Roo.log(pane);
28068         // tabs are rendere left to right..
28069         if(!this.showtabs){
28070             return;
28071         }
28072         
28073         var ctr = this.el.select('.nav-tabs', true).first();
28074          
28075          
28076         var existing = ctr.select('.nav-tab',true);
28077         var qty = existing.getCount();;
28078         
28079         
28080         var tab = ctr.createChild({
28081             tag : 'li',
28082             cls : 'nav-tab' + (qty ? '' : ' active'),
28083             cn : [
28084                 {
28085                     tag : 'a',
28086                     href:'#',
28087                     html : pane.title
28088                 }
28089             ]
28090         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28091         pane.tab = tab;
28092         
28093         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28094         if (!qty) {
28095             pane.el.addClass('active');
28096         }
28097         
28098                 
28099     },
28100     onTabClick : function(ev,un,ob,pane)
28101     {
28102         //Roo.log('tab - prev default');
28103         ev.preventDefault();
28104         
28105         
28106         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28107         pane.tab.addClass('active');
28108         //Roo.log(pane.title);
28109         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28110         // technically we should have a deactivate event.. but maybe add later.
28111         // and it should not de-activate the selected tab...
28112         this.fireEvent('activatepane', pane);
28113         pane.el.addClass('active');
28114         pane.fireEvent('activate');
28115         
28116         
28117     },
28118     
28119     getActivePane : function()
28120     {
28121         var r = false;
28122         Roo.each(this.panes, function(p) {
28123             if(p.el.hasClass('active')){
28124                 r = p;
28125                 return false;
28126             }
28127             
28128             return;
28129         });
28130         
28131         return r;
28132     }
28133     
28134     
28135 });
28136
28137  
28138 /*
28139  * - LGPL
28140  *
28141  * Tab pane
28142  * 
28143  */
28144 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28145 /**
28146  * @class Roo.bootstrap.TabPane
28147  * @extends Roo.bootstrap.Component
28148  * Bootstrap TabPane class
28149  * @cfg {Boolean} active (false | true) Default false
28150  * @cfg {String} title title of panel
28151
28152  * 
28153  * @constructor
28154  * Create a new TabPane
28155  * @param {Object} config The config object
28156  */
28157
28158 Roo.bootstrap.dash.TabPane = function(config){
28159     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28160     
28161     this.addEvents({
28162         // raw events
28163         /**
28164          * @event activate
28165          * When a pane is activated
28166          * @param {Roo.bootstrap.dash.TabPane} pane
28167          */
28168         "activate" : true
28169          
28170     });
28171 };
28172
28173 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28174     
28175     active : false,
28176     title : '',
28177     
28178     // the tabBox that this is attached to.
28179     tab : false,
28180      
28181     getAutoCreate : function() 
28182     {
28183         var cfg = {
28184             tag: 'div',
28185             cls: 'tab-pane'
28186         };
28187         
28188         if(this.active){
28189             cfg.cls += ' active';
28190         }
28191         
28192         return cfg;
28193     },
28194     initEvents  : function()
28195     {
28196         //Roo.log('trigger add pane handler');
28197         this.parent().fireEvent('addpane', this)
28198     },
28199     
28200      /**
28201      * Updates the tab title 
28202      * @param {String} html to set the title to.
28203      */
28204     setTitle: function(str)
28205     {
28206         if (!this.tab) {
28207             return;
28208         }
28209         this.title = str;
28210         this.tab.select('a', true).first().dom.innerHTML = str;
28211         
28212     }
28213     
28214     
28215     
28216 });
28217
28218  
28219
28220
28221  /*
28222  * - LGPL
28223  *
28224  * menu
28225  * 
28226  */
28227 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28228
28229 /**
28230  * @class Roo.bootstrap.menu.Menu
28231  * @extends Roo.bootstrap.Component
28232  * Bootstrap Menu class - container for Menu
28233  * @cfg {String} html Text of the menu
28234  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28235  * @cfg {String} icon Font awesome icon
28236  * @cfg {String} pos Menu align to (top | bottom) default bottom
28237  * 
28238  * 
28239  * @constructor
28240  * Create a new Menu
28241  * @param {Object} config The config object
28242  */
28243
28244
28245 Roo.bootstrap.menu.Menu = function(config){
28246     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28247     
28248     this.addEvents({
28249         /**
28250          * @event beforeshow
28251          * Fires before this menu is displayed
28252          * @param {Roo.bootstrap.menu.Menu} this
28253          */
28254         beforeshow : true,
28255         /**
28256          * @event beforehide
28257          * Fires before this menu is hidden
28258          * @param {Roo.bootstrap.menu.Menu} this
28259          */
28260         beforehide : true,
28261         /**
28262          * @event show
28263          * Fires after this menu is displayed
28264          * @param {Roo.bootstrap.menu.Menu} this
28265          */
28266         show : true,
28267         /**
28268          * @event hide
28269          * Fires after this menu is hidden
28270          * @param {Roo.bootstrap.menu.Menu} this
28271          */
28272         hide : true,
28273         /**
28274          * @event click
28275          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28276          * @param {Roo.bootstrap.menu.Menu} this
28277          * @param {Roo.EventObject} e
28278          */
28279         click : true
28280     });
28281     
28282 };
28283
28284 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28285     
28286     submenu : false,
28287     html : '',
28288     weight : 'default',
28289     icon : false,
28290     pos : 'bottom',
28291     
28292     
28293     getChildContainer : function() {
28294         if(this.isSubMenu){
28295             return this.el;
28296         }
28297         
28298         return this.el.select('ul.dropdown-menu', true).first();  
28299     },
28300     
28301     getAutoCreate : function()
28302     {
28303         var text = [
28304             {
28305                 tag : 'span',
28306                 cls : 'roo-menu-text',
28307                 html : this.html
28308             }
28309         ];
28310         
28311         if(this.icon){
28312             text.unshift({
28313                 tag : 'i',
28314                 cls : 'fa ' + this.icon
28315             })
28316         }
28317         
28318         
28319         var cfg = {
28320             tag : 'div',
28321             cls : 'btn-group',
28322             cn : [
28323                 {
28324                     tag : 'button',
28325                     cls : 'dropdown-button btn btn-' + this.weight,
28326                     cn : text
28327                 },
28328                 {
28329                     tag : 'button',
28330                     cls : 'dropdown-toggle btn btn-' + this.weight,
28331                     cn : [
28332                         {
28333                             tag : 'span',
28334                             cls : 'caret'
28335                         }
28336                     ]
28337                 },
28338                 {
28339                     tag : 'ul',
28340                     cls : 'dropdown-menu'
28341                 }
28342             ]
28343             
28344         };
28345         
28346         if(this.pos == 'top'){
28347             cfg.cls += ' dropup';
28348         }
28349         
28350         if(this.isSubMenu){
28351             cfg = {
28352                 tag : 'ul',
28353                 cls : 'dropdown-menu'
28354             }
28355         }
28356         
28357         return cfg;
28358     },
28359     
28360     onRender : function(ct, position)
28361     {
28362         this.isSubMenu = ct.hasClass('dropdown-submenu');
28363         
28364         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28365     },
28366     
28367     initEvents : function() 
28368     {
28369         if(this.isSubMenu){
28370             return;
28371         }
28372         
28373         this.hidden = true;
28374         
28375         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28376         this.triggerEl.on('click', this.onTriggerPress, this);
28377         
28378         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28379         this.buttonEl.on('click', this.onClick, this);
28380         
28381     },
28382     
28383     list : function()
28384     {
28385         if(this.isSubMenu){
28386             return this.el;
28387         }
28388         
28389         return this.el.select('ul.dropdown-menu', true).first();
28390     },
28391     
28392     onClick : function(e)
28393     {
28394         this.fireEvent("click", this, e);
28395     },
28396     
28397     onTriggerPress  : function(e)
28398     {   
28399         if (this.isVisible()) {
28400             this.hide();
28401         } else {
28402             this.show();
28403         }
28404     },
28405     
28406     isVisible : function(){
28407         return !this.hidden;
28408     },
28409     
28410     show : function()
28411     {
28412         this.fireEvent("beforeshow", this);
28413         
28414         this.hidden = false;
28415         this.el.addClass('open');
28416         
28417         Roo.get(document).on("mouseup", this.onMouseUp, this);
28418         
28419         this.fireEvent("show", this);
28420         
28421         
28422     },
28423     
28424     hide : function()
28425     {
28426         this.fireEvent("beforehide", this);
28427         
28428         this.hidden = true;
28429         this.el.removeClass('open');
28430         
28431         Roo.get(document).un("mouseup", this.onMouseUp);
28432         
28433         this.fireEvent("hide", this);
28434     },
28435     
28436     onMouseUp : function()
28437     {
28438         this.hide();
28439     }
28440     
28441 });
28442
28443  
28444  /*
28445  * - LGPL
28446  *
28447  * menu item
28448  * 
28449  */
28450 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28451
28452 /**
28453  * @class Roo.bootstrap.menu.Item
28454  * @extends Roo.bootstrap.Component
28455  * Bootstrap MenuItem class
28456  * @cfg {Boolean} submenu (true | false) default false
28457  * @cfg {String} html text of the item
28458  * @cfg {String} href the link
28459  * @cfg {Boolean} disable (true | false) default false
28460  * @cfg {Boolean} preventDefault (true | false) default true
28461  * @cfg {String} icon Font awesome icon
28462  * @cfg {String} pos Submenu align to (left | right) default right 
28463  * 
28464  * 
28465  * @constructor
28466  * Create a new Item
28467  * @param {Object} config The config object
28468  */
28469
28470
28471 Roo.bootstrap.menu.Item = function(config){
28472     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28473     this.addEvents({
28474         /**
28475          * @event mouseover
28476          * Fires when the mouse is hovering over this menu
28477          * @param {Roo.bootstrap.menu.Item} this
28478          * @param {Roo.EventObject} e
28479          */
28480         mouseover : true,
28481         /**
28482          * @event mouseout
28483          * Fires when the mouse exits this menu
28484          * @param {Roo.bootstrap.menu.Item} this
28485          * @param {Roo.EventObject} e
28486          */
28487         mouseout : true,
28488         // raw events
28489         /**
28490          * @event click
28491          * The raw click event for the entire grid.
28492          * @param {Roo.EventObject} e
28493          */
28494         click : true
28495     });
28496 };
28497
28498 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28499     
28500     submenu : false,
28501     href : '',
28502     html : '',
28503     preventDefault: true,
28504     disable : false,
28505     icon : false,
28506     pos : 'right',
28507     
28508     getAutoCreate : function()
28509     {
28510         var text = [
28511             {
28512                 tag : 'span',
28513                 cls : 'roo-menu-item-text',
28514                 html : this.html
28515             }
28516         ];
28517         
28518         if(this.icon){
28519             text.unshift({
28520                 tag : 'i',
28521                 cls : 'fa ' + this.icon
28522             })
28523         }
28524         
28525         var cfg = {
28526             tag : 'li',
28527             cn : [
28528                 {
28529                     tag : 'a',
28530                     href : this.href || '#',
28531                     cn : text
28532                 }
28533             ]
28534         };
28535         
28536         if(this.disable){
28537             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28538         }
28539         
28540         if(this.submenu){
28541             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28542             
28543             if(this.pos == 'left'){
28544                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28545             }
28546         }
28547         
28548         return cfg;
28549     },
28550     
28551     initEvents : function() 
28552     {
28553         this.el.on('mouseover', this.onMouseOver, this);
28554         this.el.on('mouseout', this.onMouseOut, this);
28555         
28556         this.el.select('a', true).first().on('click', this.onClick, this);
28557         
28558     },
28559     
28560     onClick : function(e)
28561     {
28562         if(this.preventDefault){
28563             e.preventDefault();
28564         }
28565         
28566         this.fireEvent("click", this, e);
28567     },
28568     
28569     onMouseOver : function(e)
28570     {
28571         if(this.submenu && this.pos == 'left'){
28572             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28573         }
28574         
28575         this.fireEvent("mouseover", this, e);
28576     },
28577     
28578     onMouseOut : function(e)
28579     {
28580         this.fireEvent("mouseout", this, e);
28581     }
28582 });
28583
28584  
28585
28586  /*
28587  * - LGPL
28588  *
28589  * menu separator
28590  * 
28591  */
28592 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28593
28594 /**
28595  * @class Roo.bootstrap.menu.Separator
28596  * @extends Roo.bootstrap.Component
28597  * Bootstrap Separator class
28598  * 
28599  * @constructor
28600  * Create a new Separator
28601  * @param {Object} config The config object
28602  */
28603
28604
28605 Roo.bootstrap.menu.Separator = function(config){
28606     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28607 };
28608
28609 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28610     
28611     getAutoCreate : function(){
28612         var cfg = {
28613             tag : 'li',
28614             cls: 'divider'
28615         };
28616         
28617         return cfg;
28618     }
28619    
28620 });
28621
28622  
28623
28624  /*
28625  * - LGPL
28626  *
28627  * Tooltip
28628  * 
28629  */
28630
28631 /**
28632  * @class Roo.bootstrap.Tooltip
28633  * Bootstrap Tooltip class
28634  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28635  * to determine which dom element triggers the tooltip.
28636  * 
28637  * It needs to add support for additional attributes like tooltip-position
28638  * 
28639  * @constructor
28640  * Create a new Toolti
28641  * @param {Object} config The config object
28642  */
28643
28644 Roo.bootstrap.Tooltip = function(config){
28645     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28646     
28647     this.alignment = Roo.bootstrap.Tooltip.alignment;
28648     
28649     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28650         this.alignment = config.alignment;
28651     }
28652     
28653 };
28654
28655 Roo.apply(Roo.bootstrap.Tooltip, {
28656     /**
28657      * @function init initialize tooltip monitoring.
28658      * @static
28659      */
28660     currentEl : false,
28661     currentTip : false,
28662     currentRegion : false,
28663     
28664     //  init : delay?
28665     
28666     init : function()
28667     {
28668         Roo.get(document).on('mouseover', this.enter ,this);
28669         Roo.get(document).on('mouseout', this.leave, this);
28670          
28671         
28672         this.currentTip = new Roo.bootstrap.Tooltip();
28673     },
28674     
28675     enter : function(ev)
28676     {
28677         var dom = ev.getTarget();
28678         
28679         //Roo.log(['enter',dom]);
28680         var el = Roo.fly(dom);
28681         if (this.currentEl) {
28682             //Roo.log(dom);
28683             //Roo.log(this.currentEl);
28684             //Roo.log(this.currentEl.contains(dom));
28685             if (this.currentEl == el) {
28686                 return;
28687             }
28688             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28689                 return;
28690             }
28691
28692         }
28693         
28694         if (this.currentTip.el) {
28695             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28696         }    
28697         //Roo.log(ev);
28698         
28699         if(!el || el.dom == document){
28700             return;
28701         }
28702         
28703         var bindEl = el;
28704         
28705         // you can not look for children, as if el is the body.. then everythign is the child..
28706         if (!el.attr('tooltip')) { //
28707             if (!el.select("[tooltip]").elements.length) {
28708                 return;
28709             }
28710             // is the mouse over this child...?
28711             bindEl = el.select("[tooltip]").first();
28712             var xy = ev.getXY();
28713             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28714                 //Roo.log("not in region.");
28715                 return;
28716             }
28717             //Roo.log("child element over..");
28718             
28719         }
28720         this.currentEl = bindEl;
28721         this.currentTip.bind(bindEl);
28722         this.currentRegion = Roo.lib.Region.getRegion(dom);
28723         this.currentTip.enter();
28724         
28725     },
28726     leave : function(ev)
28727     {
28728         var dom = ev.getTarget();
28729         //Roo.log(['leave',dom]);
28730         if (!this.currentEl) {
28731             return;
28732         }
28733         
28734         
28735         if (dom != this.currentEl.dom) {
28736             return;
28737         }
28738         var xy = ev.getXY();
28739         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28740             return;
28741         }
28742         // only activate leave if mouse cursor is outside... bounding box..
28743         
28744         
28745         
28746         
28747         if (this.currentTip) {
28748             this.currentTip.leave();
28749         }
28750         //Roo.log('clear currentEl');
28751         this.currentEl = false;
28752         
28753         
28754     },
28755     alignment : {
28756         'left' : ['r-l', [-2,0], 'right'],
28757         'right' : ['l-r', [2,0], 'left'],
28758         'bottom' : ['t-b', [0,2], 'top'],
28759         'top' : [ 'b-t', [0,-2], 'bottom']
28760     }
28761     
28762 });
28763
28764
28765 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28766     
28767     
28768     bindEl : false,
28769     
28770     delay : null, // can be { show : 300 , hide: 500}
28771     
28772     timeout : null,
28773     
28774     hoverState : null, //???
28775     
28776     placement : 'bottom', 
28777     
28778     alignment : false,
28779     
28780     getAutoCreate : function(){
28781     
28782         var cfg = {
28783            cls : 'tooltip',   
28784            role : 'tooltip',
28785            cn : [
28786                 {
28787                     cls : 'tooltip-arrow arrow'
28788                 },
28789                 {
28790                     cls : 'tooltip-inner'
28791                 }
28792            ]
28793         };
28794         
28795         return cfg;
28796     },
28797     bind : function(el)
28798     {
28799         this.bindEl = el;
28800     },
28801     
28802     initEvents : function()
28803     {
28804         this.arrowEl = this.el.select('.arrow', true).first();
28805         this.innerEl = this.el.select('.tooltip-inner', true).first();
28806     },
28807     
28808     enter : function () {
28809        
28810         if (this.timeout != null) {
28811             clearTimeout(this.timeout);
28812         }
28813         
28814         this.hoverState = 'in';
28815          //Roo.log("enter - show");
28816         if (!this.delay || !this.delay.show) {
28817             this.show();
28818             return;
28819         }
28820         var _t = this;
28821         this.timeout = setTimeout(function () {
28822             if (_t.hoverState == 'in') {
28823                 _t.show();
28824             }
28825         }, this.delay.show);
28826     },
28827     leave : function()
28828     {
28829         clearTimeout(this.timeout);
28830     
28831         this.hoverState = 'out';
28832          if (!this.delay || !this.delay.hide) {
28833             this.hide();
28834             return;
28835         }
28836        
28837         var _t = this;
28838         this.timeout = setTimeout(function () {
28839             //Roo.log("leave - timeout");
28840             
28841             if (_t.hoverState == 'out') {
28842                 _t.hide();
28843                 Roo.bootstrap.Tooltip.currentEl = false;
28844             }
28845         }, delay);
28846     },
28847     
28848     show : function (msg)
28849     {
28850         if (!this.el) {
28851             this.render(document.body);
28852         }
28853         // set content.
28854         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28855         
28856         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28857         
28858         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28859         
28860         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28861                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28862         
28863         var placement = typeof this.placement == 'function' ?
28864             this.placement.call(this, this.el, on_el) :
28865             this.placement;
28866             
28867         var autoToken = /\s?auto?\s?/i;
28868         var autoPlace = autoToken.test(placement);
28869         if (autoPlace) {
28870             placement = placement.replace(autoToken, '') || 'top';
28871         }
28872         
28873         //this.el.detach()
28874         //this.el.setXY([0,0]);
28875         this.el.show();
28876         //this.el.dom.style.display='block';
28877         
28878         //this.el.appendTo(on_el);
28879         
28880         var p = this.getPosition();
28881         var box = this.el.getBox();
28882         
28883         if (autoPlace) {
28884             // fixme..
28885         }
28886         
28887         var align = this.alignment[placement];
28888         
28889         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28890         
28891         if(placement == 'top' || placement == 'bottom'){
28892             if(xy[0] < 0){
28893                 placement = 'right';
28894             }
28895             
28896             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28897                 placement = 'left';
28898             }
28899             
28900             var scroll = Roo.select('body', true).first().getScroll();
28901             
28902             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28903                 placement = 'top';
28904             }
28905             
28906             align = this.alignment[placement];
28907             
28908             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28909             
28910         }
28911         
28912         this.el.alignTo(this.bindEl, align[0],align[1]);
28913         //var arrow = this.el.select('.arrow',true).first();
28914         //arrow.set(align[2], 
28915         
28916         this.el.addClass(placement);
28917         this.el.addClass("bs-tooltip-"+ placement);
28918         
28919         this.el.addClass('in fade show');
28920         
28921         this.hoverState = null;
28922         
28923         if (this.el.hasClass('fade')) {
28924             // fade it?
28925         }
28926         
28927         
28928         
28929         
28930         
28931     },
28932     hide : function()
28933     {
28934          
28935         if (!this.el) {
28936             return;
28937         }
28938         //this.el.setXY([0,0]);
28939         this.el.removeClass(['show', 'in']);
28940         //this.el.hide();
28941         
28942     }
28943     
28944 });
28945  
28946
28947  /*
28948  * - LGPL
28949  *
28950  * Location Picker
28951  * 
28952  */
28953
28954 /**
28955  * @class Roo.bootstrap.LocationPicker
28956  * @extends Roo.bootstrap.Component
28957  * Bootstrap LocationPicker class
28958  * @cfg {Number} latitude Position when init default 0
28959  * @cfg {Number} longitude Position when init default 0
28960  * @cfg {Number} zoom default 15
28961  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28962  * @cfg {Boolean} mapTypeControl default false
28963  * @cfg {Boolean} disableDoubleClickZoom default false
28964  * @cfg {Boolean} scrollwheel default true
28965  * @cfg {Boolean} streetViewControl default false
28966  * @cfg {Number} radius default 0
28967  * @cfg {String} locationName
28968  * @cfg {Boolean} draggable default true
28969  * @cfg {Boolean} enableAutocomplete default false
28970  * @cfg {Boolean} enableReverseGeocode default true
28971  * @cfg {String} markerTitle
28972  * 
28973  * @constructor
28974  * Create a new LocationPicker
28975  * @param {Object} config The config object
28976  */
28977
28978
28979 Roo.bootstrap.LocationPicker = function(config){
28980     
28981     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28982     
28983     this.addEvents({
28984         /**
28985          * @event initial
28986          * Fires when the picker initialized.
28987          * @param {Roo.bootstrap.LocationPicker} this
28988          * @param {Google Location} location
28989          */
28990         initial : true,
28991         /**
28992          * @event positionchanged
28993          * Fires when the picker position changed.
28994          * @param {Roo.bootstrap.LocationPicker} this
28995          * @param {Google Location} location
28996          */
28997         positionchanged : true,
28998         /**
28999          * @event resize
29000          * Fires when the map resize.
29001          * @param {Roo.bootstrap.LocationPicker} this
29002          */
29003         resize : true,
29004         /**
29005          * @event show
29006          * Fires when the map show.
29007          * @param {Roo.bootstrap.LocationPicker} this
29008          */
29009         show : true,
29010         /**
29011          * @event hide
29012          * Fires when the map hide.
29013          * @param {Roo.bootstrap.LocationPicker} this
29014          */
29015         hide : true,
29016         /**
29017          * @event mapClick
29018          * Fires when click the map.
29019          * @param {Roo.bootstrap.LocationPicker} this
29020          * @param {Map event} e
29021          */
29022         mapClick : true,
29023         /**
29024          * @event mapRightClick
29025          * Fires when right click the map.
29026          * @param {Roo.bootstrap.LocationPicker} this
29027          * @param {Map event} e
29028          */
29029         mapRightClick : true,
29030         /**
29031          * @event markerClick
29032          * Fires when click the marker.
29033          * @param {Roo.bootstrap.LocationPicker} this
29034          * @param {Map event} e
29035          */
29036         markerClick : true,
29037         /**
29038          * @event markerRightClick
29039          * Fires when right click the marker.
29040          * @param {Roo.bootstrap.LocationPicker} this
29041          * @param {Map event} e
29042          */
29043         markerRightClick : true,
29044         /**
29045          * @event OverlayViewDraw
29046          * Fires when OverlayView Draw
29047          * @param {Roo.bootstrap.LocationPicker} this
29048          */
29049         OverlayViewDraw : true,
29050         /**
29051          * @event OverlayViewOnAdd
29052          * Fires when OverlayView Draw
29053          * @param {Roo.bootstrap.LocationPicker} this
29054          */
29055         OverlayViewOnAdd : true,
29056         /**
29057          * @event OverlayViewOnRemove
29058          * Fires when OverlayView Draw
29059          * @param {Roo.bootstrap.LocationPicker} this
29060          */
29061         OverlayViewOnRemove : true,
29062         /**
29063          * @event OverlayViewShow
29064          * Fires when OverlayView Draw
29065          * @param {Roo.bootstrap.LocationPicker} this
29066          * @param {Pixel} cpx
29067          */
29068         OverlayViewShow : true,
29069         /**
29070          * @event OverlayViewHide
29071          * Fires when OverlayView Draw
29072          * @param {Roo.bootstrap.LocationPicker} this
29073          */
29074         OverlayViewHide : true,
29075         /**
29076          * @event loadexception
29077          * Fires when load google lib failed.
29078          * @param {Roo.bootstrap.LocationPicker} this
29079          */
29080         loadexception : true
29081     });
29082         
29083 };
29084
29085 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29086     
29087     gMapContext: false,
29088     
29089     latitude: 0,
29090     longitude: 0,
29091     zoom: 15,
29092     mapTypeId: false,
29093     mapTypeControl: false,
29094     disableDoubleClickZoom: false,
29095     scrollwheel: true,
29096     streetViewControl: false,
29097     radius: 0,
29098     locationName: '',
29099     draggable: true,
29100     enableAutocomplete: false,
29101     enableReverseGeocode: true,
29102     markerTitle: '',
29103     
29104     getAutoCreate: function()
29105     {
29106
29107         var cfg = {
29108             tag: 'div',
29109             cls: 'roo-location-picker'
29110         };
29111         
29112         return cfg
29113     },
29114     
29115     initEvents: function(ct, position)
29116     {       
29117         if(!this.el.getWidth() || this.isApplied()){
29118             return;
29119         }
29120         
29121         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29122         
29123         this.initial();
29124     },
29125     
29126     initial: function()
29127     {
29128         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29129             this.fireEvent('loadexception', this);
29130             return;
29131         }
29132         
29133         if(!this.mapTypeId){
29134             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29135         }
29136         
29137         this.gMapContext = this.GMapContext();
29138         
29139         this.initOverlayView();
29140         
29141         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29142         
29143         var _this = this;
29144                 
29145         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29146             _this.setPosition(_this.gMapContext.marker.position);
29147         });
29148         
29149         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29150             _this.fireEvent('mapClick', this, event);
29151             
29152         });
29153
29154         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29155             _this.fireEvent('mapRightClick', this, event);
29156             
29157         });
29158         
29159         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29160             _this.fireEvent('markerClick', this, event);
29161             
29162         });
29163
29164         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29165             _this.fireEvent('markerRightClick', this, event);
29166             
29167         });
29168         
29169         this.setPosition(this.gMapContext.location);
29170         
29171         this.fireEvent('initial', this, this.gMapContext.location);
29172     },
29173     
29174     initOverlayView: function()
29175     {
29176         var _this = this;
29177         
29178         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29179             
29180             draw: function()
29181             {
29182                 _this.fireEvent('OverlayViewDraw', _this);
29183             },
29184             
29185             onAdd: function()
29186             {
29187                 _this.fireEvent('OverlayViewOnAdd', _this);
29188             },
29189             
29190             onRemove: function()
29191             {
29192                 _this.fireEvent('OverlayViewOnRemove', _this);
29193             },
29194             
29195             show: function(cpx)
29196             {
29197                 _this.fireEvent('OverlayViewShow', _this, cpx);
29198             },
29199             
29200             hide: function()
29201             {
29202                 _this.fireEvent('OverlayViewHide', _this);
29203             }
29204             
29205         });
29206     },
29207     
29208     fromLatLngToContainerPixel: function(event)
29209     {
29210         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29211     },
29212     
29213     isApplied: function() 
29214     {
29215         return this.getGmapContext() == false ? false : true;
29216     },
29217     
29218     getGmapContext: function() 
29219     {
29220         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29221     },
29222     
29223     GMapContext: function() 
29224     {
29225         var position = new google.maps.LatLng(this.latitude, this.longitude);
29226         
29227         var _map = new google.maps.Map(this.el.dom, {
29228             center: position,
29229             zoom: this.zoom,
29230             mapTypeId: this.mapTypeId,
29231             mapTypeControl: this.mapTypeControl,
29232             disableDoubleClickZoom: this.disableDoubleClickZoom,
29233             scrollwheel: this.scrollwheel,
29234             streetViewControl: this.streetViewControl,
29235             locationName: this.locationName,
29236             draggable: this.draggable,
29237             enableAutocomplete: this.enableAutocomplete,
29238             enableReverseGeocode: this.enableReverseGeocode
29239         });
29240         
29241         var _marker = new google.maps.Marker({
29242             position: position,
29243             map: _map,
29244             title: this.markerTitle,
29245             draggable: this.draggable
29246         });
29247         
29248         return {
29249             map: _map,
29250             marker: _marker,
29251             circle: null,
29252             location: position,
29253             radius: this.radius,
29254             locationName: this.locationName,
29255             addressComponents: {
29256                 formatted_address: null,
29257                 addressLine1: null,
29258                 addressLine2: null,
29259                 streetName: null,
29260                 streetNumber: null,
29261                 city: null,
29262                 district: null,
29263                 state: null,
29264                 stateOrProvince: null
29265             },
29266             settings: this,
29267             domContainer: this.el.dom,
29268             geodecoder: new google.maps.Geocoder()
29269         };
29270     },
29271     
29272     drawCircle: function(center, radius, options) 
29273     {
29274         if (this.gMapContext.circle != null) {
29275             this.gMapContext.circle.setMap(null);
29276         }
29277         if (radius > 0) {
29278             radius *= 1;
29279             options = Roo.apply({}, options, {
29280                 strokeColor: "#0000FF",
29281                 strokeOpacity: .35,
29282                 strokeWeight: 2,
29283                 fillColor: "#0000FF",
29284                 fillOpacity: .2
29285             });
29286             
29287             options.map = this.gMapContext.map;
29288             options.radius = radius;
29289             options.center = center;
29290             this.gMapContext.circle = new google.maps.Circle(options);
29291             return this.gMapContext.circle;
29292         }
29293         
29294         return null;
29295     },
29296     
29297     setPosition: function(location) 
29298     {
29299         this.gMapContext.location = location;
29300         this.gMapContext.marker.setPosition(location);
29301         this.gMapContext.map.panTo(location);
29302         this.drawCircle(location, this.gMapContext.radius, {});
29303         
29304         var _this = this;
29305         
29306         if (this.gMapContext.settings.enableReverseGeocode) {
29307             this.gMapContext.geodecoder.geocode({
29308                 latLng: this.gMapContext.location
29309             }, function(results, status) {
29310                 
29311                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29312                     _this.gMapContext.locationName = results[0].formatted_address;
29313                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29314                     
29315                     _this.fireEvent('positionchanged', this, location);
29316                 }
29317             });
29318             
29319             return;
29320         }
29321         
29322         this.fireEvent('positionchanged', this, location);
29323     },
29324     
29325     resize: function()
29326     {
29327         google.maps.event.trigger(this.gMapContext.map, "resize");
29328         
29329         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29330         
29331         this.fireEvent('resize', this);
29332     },
29333     
29334     setPositionByLatLng: function(latitude, longitude)
29335     {
29336         this.setPosition(new google.maps.LatLng(latitude, longitude));
29337     },
29338     
29339     getCurrentPosition: function() 
29340     {
29341         return {
29342             latitude: this.gMapContext.location.lat(),
29343             longitude: this.gMapContext.location.lng()
29344         };
29345     },
29346     
29347     getAddressName: function() 
29348     {
29349         return this.gMapContext.locationName;
29350     },
29351     
29352     getAddressComponents: function() 
29353     {
29354         return this.gMapContext.addressComponents;
29355     },
29356     
29357     address_component_from_google_geocode: function(address_components) 
29358     {
29359         var result = {};
29360         
29361         for (var i = 0; i < address_components.length; i++) {
29362             var component = address_components[i];
29363             if (component.types.indexOf("postal_code") >= 0) {
29364                 result.postalCode = component.short_name;
29365             } else if (component.types.indexOf("street_number") >= 0) {
29366                 result.streetNumber = component.short_name;
29367             } else if (component.types.indexOf("route") >= 0) {
29368                 result.streetName = component.short_name;
29369             } else if (component.types.indexOf("neighborhood") >= 0) {
29370                 result.city = component.short_name;
29371             } else if (component.types.indexOf("locality") >= 0) {
29372                 result.city = component.short_name;
29373             } else if (component.types.indexOf("sublocality") >= 0) {
29374                 result.district = component.short_name;
29375             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29376                 result.stateOrProvince = component.short_name;
29377             } else if (component.types.indexOf("country") >= 0) {
29378                 result.country = component.short_name;
29379             }
29380         }
29381         
29382         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29383         result.addressLine2 = "";
29384         return result;
29385     },
29386     
29387     setZoomLevel: function(zoom)
29388     {
29389         this.gMapContext.map.setZoom(zoom);
29390     },
29391     
29392     show: function()
29393     {
29394         if(!this.el){
29395             return;
29396         }
29397         
29398         this.el.show();
29399         
29400         this.resize();
29401         
29402         this.fireEvent('show', this);
29403     },
29404     
29405     hide: function()
29406     {
29407         if(!this.el){
29408             return;
29409         }
29410         
29411         this.el.hide();
29412         
29413         this.fireEvent('hide', this);
29414     }
29415     
29416 });
29417
29418 Roo.apply(Roo.bootstrap.LocationPicker, {
29419     
29420     OverlayView : function(map, options)
29421     {
29422         options = options || {};
29423         
29424         this.setMap(map);
29425     }
29426     
29427     
29428 });/**
29429  * @class Roo.bootstrap.Alert
29430  * @extends Roo.bootstrap.Component
29431  * Bootstrap Alert class - shows an alert area box
29432  * eg
29433  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29434   Enter a valid email address
29435 </div>
29436  * @licence LGPL
29437  * @cfg {String} title The title of alert
29438  * @cfg {String} html The content of alert
29439  * @cfg {String} weight (  success | info | warning | danger )
29440  * @cfg {String} faicon font-awesomeicon
29441  * 
29442  * @constructor
29443  * Create a new alert
29444  * @param {Object} config The config object
29445  */
29446
29447
29448 Roo.bootstrap.Alert = function(config){
29449     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29450     
29451 };
29452
29453 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29454     
29455     title: '',
29456     html: '',
29457     weight: false,
29458     faicon: false,
29459     
29460     getAutoCreate : function()
29461     {
29462         
29463         var cfg = {
29464             tag : 'div',
29465             cls : 'alert',
29466             cn : [
29467                 {
29468                     tag : 'i',
29469                     cls : 'roo-alert-icon'
29470                     
29471                 },
29472                 {
29473                     tag : 'b',
29474                     cls : 'roo-alert-title',
29475                     html : this.title
29476                 },
29477                 {
29478                     tag : 'span',
29479                     cls : 'roo-alert-text',
29480                     html : this.html
29481                 }
29482             ]
29483         };
29484         
29485         if(this.faicon){
29486             cfg.cn[0].cls += ' fa ' + this.faicon;
29487         }
29488         
29489         if(this.weight){
29490             cfg.cls += ' alert-' + this.weight;
29491         }
29492         
29493         return cfg;
29494     },
29495     
29496     initEvents: function() 
29497     {
29498         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29499     },
29500     
29501     setTitle : function(str)
29502     {
29503         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29504     },
29505     
29506     setText : function(str)
29507     {
29508         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29509     },
29510     
29511     setWeight : function(weight)
29512     {
29513         if(this.weight){
29514             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29515         }
29516         
29517         this.weight = weight;
29518         
29519         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29520     },
29521     
29522     setIcon : function(icon)
29523     {
29524         if(this.faicon){
29525             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29526         }
29527         
29528         this.faicon = icon;
29529         
29530         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29531     },
29532     
29533     hide: function() 
29534     {
29535         this.el.hide();   
29536     },
29537     
29538     show: function() 
29539     {  
29540         this.el.show();   
29541     }
29542     
29543 });
29544
29545  
29546 /*
29547 * Licence: LGPL
29548 */
29549
29550 /**
29551  * @class Roo.bootstrap.UploadCropbox
29552  * @extends Roo.bootstrap.Component
29553  * Bootstrap UploadCropbox class
29554  * @cfg {String} emptyText show when image has been loaded
29555  * @cfg {String} rotateNotify show when image too small to rotate
29556  * @cfg {Number} errorTimeout default 3000
29557  * @cfg {Number} minWidth default 300
29558  * @cfg {Number} minHeight default 300
29559  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29560  * @cfg {Boolean} isDocument (true|false) default false
29561  * @cfg {String} url action url
29562  * @cfg {String} paramName default 'imageUpload'
29563  * @cfg {String} method default POST
29564  * @cfg {Boolean} loadMask (true|false) default true
29565  * @cfg {Boolean} loadingText default 'Loading...'
29566  * 
29567  * @constructor
29568  * Create a new UploadCropbox
29569  * @param {Object} config The config object
29570  */
29571
29572 Roo.bootstrap.UploadCropbox = function(config){
29573     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29574     
29575     this.addEvents({
29576         /**
29577          * @event beforeselectfile
29578          * Fire before select file
29579          * @param {Roo.bootstrap.UploadCropbox} this
29580          */
29581         "beforeselectfile" : true,
29582         /**
29583          * @event initial
29584          * Fire after initEvent
29585          * @param {Roo.bootstrap.UploadCropbox} this
29586          */
29587         "initial" : true,
29588         /**
29589          * @event crop
29590          * Fire after initEvent
29591          * @param {Roo.bootstrap.UploadCropbox} this
29592          * @param {String} data
29593          */
29594         "crop" : true,
29595         /**
29596          * @event prepare
29597          * Fire when preparing the file data
29598          * @param {Roo.bootstrap.UploadCropbox} this
29599          * @param {Object} file
29600          */
29601         "prepare" : true,
29602         /**
29603          * @event exception
29604          * Fire when get exception
29605          * @param {Roo.bootstrap.UploadCropbox} this
29606          * @param {XMLHttpRequest} xhr
29607          */
29608         "exception" : true,
29609         /**
29610          * @event beforeloadcanvas
29611          * Fire before load the canvas
29612          * @param {Roo.bootstrap.UploadCropbox} this
29613          * @param {String} src
29614          */
29615         "beforeloadcanvas" : true,
29616         /**
29617          * @event trash
29618          * Fire when trash image
29619          * @param {Roo.bootstrap.UploadCropbox} this
29620          */
29621         "trash" : true,
29622         /**
29623          * @event download
29624          * Fire when download the image
29625          * @param {Roo.bootstrap.UploadCropbox} this
29626          */
29627         "download" : true,
29628         /**
29629          * @event footerbuttonclick
29630          * Fire when footerbuttonclick
29631          * @param {Roo.bootstrap.UploadCropbox} this
29632          * @param {String} type
29633          */
29634         "footerbuttonclick" : true,
29635         /**
29636          * @event resize
29637          * Fire when resize
29638          * @param {Roo.bootstrap.UploadCropbox} this
29639          */
29640         "resize" : true,
29641         /**
29642          * @event rotate
29643          * Fire when rotate the image
29644          * @param {Roo.bootstrap.UploadCropbox} this
29645          * @param {String} pos
29646          */
29647         "rotate" : true,
29648         /**
29649          * @event inspect
29650          * Fire when inspect the file
29651          * @param {Roo.bootstrap.UploadCropbox} this
29652          * @param {Object} file
29653          */
29654         "inspect" : true,
29655         /**
29656          * @event upload
29657          * Fire when xhr upload the file
29658          * @param {Roo.bootstrap.UploadCropbox} this
29659          * @param {Object} data
29660          */
29661         "upload" : true,
29662         /**
29663          * @event arrange
29664          * Fire when arrange the file data
29665          * @param {Roo.bootstrap.UploadCropbox} this
29666          * @param {Object} formData
29667          */
29668         "arrange" : true
29669     });
29670     
29671     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29672 };
29673
29674 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29675     
29676     emptyText : 'Click to upload image',
29677     rotateNotify : 'Image is too small to rotate',
29678     errorTimeout : 3000,
29679     scale : 0,
29680     baseScale : 1,
29681     rotate : 0,
29682     dragable : false,
29683     pinching : false,
29684     mouseX : 0,
29685     mouseY : 0,
29686     cropData : false,
29687     minWidth : 300,
29688     minHeight : 300,
29689     file : false,
29690     exif : {},
29691     baseRotate : 1,
29692     cropType : 'image/jpeg',
29693     buttons : false,
29694     canvasLoaded : false,
29695     isDocument : false,
29696     method : 'POST',
29697     paramName : 'imageUpload',
29698     loadMask : true,
29699     loadingText : 'Loading...',
29700     maskEl : false,
29701     
29702     getAutoCreate : function()
29703     {
29704         var cfg = {
29705             tag : 'div',
29706             cls : 'roo-upload-cropbox',
29707             cn : [
29708                 {
29709                     tag : 'input',
29710                     cls : 'roo-upload-cropbox-selector',
29711                     type : 'file'
29712                 },
29713                 {
29714                     tag : 'div',
29715                     cls : 'roo-upload-cropbox-body',
29716                     style : 'cursor:pointer',
29717                     cn : [
29718                         {
29719                             tag : 'div',
29720                             cls : 'roo-upload-cropbox-preview'
29721                         },
29722                         {
29723                             tag : 'div',
29724                             cls : 'roo-upload-cropbox-thumb'
29725                         },
29726                         {
29727                             tag : 'div',
29728                             cls : 'roo-upload-cropbox-empty-notify',
29729                             html : this.emptyText
29730                         },
29731                         {
29732                             tag : 'div',
29733                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29734                             html : this.rotateNotify
29735                         }
29736                     ]
29737                 },
29738                 {
29739                     tag : 'div',
29740                     cls : 'roo-upload-cropbox-footer',
29741                     cn : {
29742                         tag : 'div',
29743                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29744                         cn : []
29745                     }
29746                 }
29747             ]
29748         };
29749         
29750         return cfg;
29751     },
29752     
29753     onRender : function(ct, position)
29754     {
29755         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29756         
29757         if (this.buttons.length) {
29758             
29759             Roo.each(this.buttons, function(bb) {
29760                 
29761                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29762                 
29763                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29764                 
29765             }, this);
29766         }
29767         
29768         if(this.loadMask){
29769             this.maskEl = this.el;
29770         }
29771     },
29772     
29773     initEvents : function()
29774     {
29775         this.urlAPI = (window.createObjectURL && window) || 
29776                                 (window.URL && URL.revokeObjectURL && URL) || 
29777                                 (window.webkitURL && webkitURL);
29778                         
29779         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29780         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29781         
29782         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29783         this.selectorEl.hide();
29784         
29785         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29786         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29787         
29788         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29789         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29790         this.thumbEl.hide();
29791         
29792         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29793         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29794         
29795         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29796         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29797         this.errorEl.hide();
29798         
29799         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29800         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29801         this.footerEl.hide();
29802         
29803         this.setThumbBoxSize();
29804         
29805         this.bind();
29806         
29807         this.resize();
29808         
29809         this.fireEvent('initial', this);
29810     },
29811
29812     bind : function()
29813     {
29814         var _this = this;
29815         
29816         window.addEventListener("resize", function() { _this.resize(); } );
29817         
29818         this.bodyEl.on('click', this.beforeSelectFile, this);
29819         
29820         if(Roo.isTouch){
29821             this.bodyEl.on('touchstart', this.onTouchStart, this);
29822             this.bodyEl.on('touchmove', this.onTouchMove, this);
29823             this.bodyEl.on('touchend', this.onTouchEnd, this);
29824         }
29825         
29826         if(!Roo.isTouch){
29827             this.bodyEl.on('mousedown', this.onMouseDown, this);
29828             this.bodyEl.on('mousemove', this.onMouseMove, this);
29829             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29830             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29831             Roo.get(document).on('mouseup', this.onMouseUp, this);
29832         }
29833         
29834         this.selectorEl.on('change', this.onFileSelected, this);
29835     },
29836     
29837     reset : function()
29838     {    
29839         this.scale = 0;
29840         this.baseScale = 1;
29841         this.rotate = 0;
29842         this.baseRotate = 1;
29843         this.dragable = false;
29844         this.pinching = false;
29845         this.mouseX = 0;
29846         this.mouseY = 0;
29847         this.cropData = false;
29848         this.notifyEl.dom.innerHTML = this.emptyText;
29849         
29850         this.selectorEl.dom.value = '';
29851         
29852     },
29853     
29854     resize : function()
29855     {
29856         if(this.fireEvent('resize', this) != false){
29857             this.setThumbBoxPosition();
29858             this.setCanvasPosition();
29859         }
29860     },
29861     
29862     onFooterButtonClick : function(e, el, o, type)
29863     {
29864         switch (type) {
29865             case 'rotate-left' :
29866                 this.onRotateLeft(e);
29867                 break;
29868             case 'rotate-right' :
29869                 this.onRotateRight(e);
29870                 break;
29871             case 'picture' :
29872                 this.beforeSelectFile(e);
29873                 break;
29874             case 'trash' :
29875                 this.trash(e);
29876                 break;
29877             case 'crop' :
29878                 this.crop(e);
29879                 break;
29880             case 'download' :
29881                 this.download(e);
29882                 break;
29883             default :
29884                 break;
29885         }
29886         
29887         this.fireEvent('footerbuttonclick', this, type);
29888     },
29889     
29890     beforeSelectFile : function(e)
29891     {
29892         e.preventDefault();
29893         
29894         if(this.fireEvent('beforeselectfile', this) != false){
29895             this.selectorEl.dom.click();
29896         }
29897     },
29898     
29899     onFileSelected : function(e)
29900     {
29901         e.preventDefault();
29902         
29903         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29904             return;
29905         }
29906         
29907         var file = this.selectorEl.dom.files[0];
29908         
29909         if(this.fireEvent('inspect', this, file) != false){
29910             this.prepare(file);
29911         }
29912         
29913     },
29914     
29915     trash : function(e)
29916     {
29917         this.fireEvent('trash', this);
29918     },
29919     
29920     download : function(e)
29921     {
29922         this.fireEvent('download', this);
29923     },
29924     
29925     loadCanvas : function(src)
29926     {   
29927         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29928             
29929             this.reset();
29930             
29931             this.imageEl = document.createElement('img');
29932             
29933             var _this = this;
29934             
29935             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29936             
29937             this.imageEl.src = src;
29938         }
29939     },
29940     
29941     onLoadCanvas : function()
29942     {   
29943         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29944         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29945         
29946         this.bodyEl.un('click', this.beforeSelectFile, this);
29947         
29948         this.notifyEl.hide();
29949         this.thumbEl.show();
29950         this.footerEl.show();
29951         
29952         this.baseRotateLevel();
29953         
29954         if(this.isDocument){
29955             this.setThumbBoxSize();
29956         }
29957         
29958         this.setThumbBoxPosition();
29959         
29960         this.baseScaleLevel();
29961         
29962         this.draw();
29963         
29964         this.resize();
29965         
29966         this.canvasLoaded = true;
29967         
29968         if(this.loadMask){
29969             this.maskEl.unmask();
29970         }
29971         
29972     },
29973     
29974     setCanvasPosition : function()
29975     {   
29976         if(!this.canvasEl){
29977             return;
29978         }
29979         
29980         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29981         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29982         
29983         this.previewEl.setLeft(pw);
29984         this.previewEl.setTop(ph);
29985         
29986     },
29987     
29988     onMouseDown : function(e)
29989     {   
29990         e.stopEvent();
29991         
29992         this.dragable = true;
29993         this.pinching = false;
29994         
29995         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29996             this.dragable = false;
29997             return;
29998         }
29999         
30000         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30001         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30002         
30003     },
30004     
30005     onMouseMove : function(e)
30006     {   
30007         e.stopEvent();
30008         
30009         if(!this.canvasLoaded){
30010             return;
30011         }
30012         
30013         if (!this.dragable){
30014             return;
30015         }
30016         
30017         var minX = Math.ceil(this.thumbEl.getLeft(true));
30018         var minY = Math.ceil(this.thumbEl.getTop(true));
30019         
30020         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30021         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30022         
30023         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30024         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30025         
30026         x = x - this.mouseX;
30027         y = y - this.mouseY;
30028         
30029         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30030         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30031         
30032         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30033         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30034         
30035         this.previewEl.setLeft(bgX);
30036         this.previewEl.setTop(bgY);
30037         
30038         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30039         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30040     },
30041     
30042     onMouseUp : function(e)
30043     {   
30044         e.stopEvent();
30045         
30046         this.dragable = false;
30047     },
30048     
30049     onMouseWheel : function(e)
30050     {   
30051         e.stopEvent();
30052         
30053         this.startScale = this.scale;
30054         
30055         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30056         
30057         if(!this.zoomable()){
30058             this.scale = this.startScale;
30059             return;
30060         }
30061         
30062         this.draw();
30063         
30064         return;
30065     },
30066     
30067     zoomable : function()
30068     {
30069         var minScale = this.thumbEl.getWidth() / this.minWidth;
30070         
30071         if(this.minWidth < this.minHeight){
30072             minScale = this.thumbEl.getHeight() / this.minHeight;
30073         }
30074         
30075         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30076         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30077         
30078         if(
30079                 this.isDocument &&
30080                 (this.rotate == 0 || this.rotate == 180) && 
30081                 (
30082                     width > this.imageEl.OriginWidth || 
30083                     height > this.imageEl.OriginHeight ||
30084                     (width < this.minWidth && height < this.minHeight)
30085                 )
30086         ){
30087             return false;
30088         }
30089         
30090         if(
30091                 this.isDocument &&
30092                 (this.rotate == 90 || this.rotate == 270) && 
30093                 (
30094                     width > this.imageEl.OriginWidth || 
30095                     height > this.imageEl.OriginHeight ||
30096                     (width < this.minHeight && height < this.minWidth)
30097                 )
30098         ){
30099             return false;
30100         }
30101         
30102         if(
30103                 !this.isDocument &&
30104                 (this.rotate == 0 || this.rotate == 180) && 
30105                 (
30106                     width < this.minWidth || 
30107                     width > this.imageEl.OriginWidth || 
30108                     height < this.minHeight || 
30109                     height > this.imageEl.OriginHeight
30110                 )
30111         ){
30112             return false;
30113         }
30114         
30115         if(
30116                 !this.isDocument &&
30117                 (this.rotate == 90 || this.rotate == 270) && 
30118                 (
30119                     width < this.minHeight || 
30120                     width > this.imageEl.OriginWidth || 
30121                     height < this.minWidth || 
30122                     height > this.imageEl.OriginHeight
30123                 )
30124         ){
30125             return false;
30126         }
30127         
30128         return true;
30129         
30130     },
30131     
30132     onRotateLeft : function(e)
30133     {   
30134         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30135             
30136             var minScale = this.thumbEl.getWidth() / this.minWidth;
30137             
30138             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30139             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30140             
30141             this.startScale = this.scale;
30142             
30143             while (this.getScaleLevel() < minScale){
30144             
30145                 this.scale = this.scale + 1;
30146                 
30147                 if(!this.zoomable()){
30148                     break;
30149                 }
30150                 
30151                 if(
30152                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30153                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30154                 ){
30155                     continue;
30156                 }
30157                 
30158                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30159
30160                 this.draw();
30161                 
30162                 return;
30163             }
30164             
30165             this.scale = this.startScale;
30166             
30167             this.onRotateFail();
30168             
30169             return false;
30170         }
30171         
30172         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30173
30174         if(this.isDocument){
30175             this.setThumbBoxSize();
30176             this.setThumbBoxPosition();
30177             this.setCanvasPosition();
30178         }
30179         
30180         this.draw();
30181         
30182         this.fireEvent('rotate', this, 'left');
30183         
30184     },
30185     
30186     onRotateRight : function(e)
30187     {
30188         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30189             
30190             var minScale = this.thumbEl.getWidth() / this.minWidth;
30191         
30192             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30193             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30194             
30195             this.startScale = this.scale;
30196             
30197             while (this.getScaleLevel() < minScale){
30198             
30199                 this.scale = this.scale + 1;
30200                 
30201                 if(!this.zoomable()){
30202                     break;
30203                 }
30204                 
30205                 if(
30206                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30207                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30208                 ){
30209                     continue;
30210                 }
30211                 
30212                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30213
30214                 this.draw();
30215                 
30216                 return;
30217             }
30218             
30219             this.scale = this.startScale;
30220             
30221             this.onRotateFail();
30222             
30223             return false;
30224         }
30225         
30226         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30227
30228         if(this.isDocument){
30229             this.setThumbBoxSize();
30230             this.setThumbBoxPosition();
30231             this.setCanvasPosition();
30232         }
30233         
30234         this.draw();
30235         
30236         this.fireEvent('rotate', this, 'right');
30237     },
30238     
30239     onRotateFail : function()
30240     {
30241         this.errorEl.show(true);
30242         
30243         var _this = this;
30244         
30245         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30246     },
30247     
30248     draw : function()
30249     {
30250         this.previewEl.dom.innerHTML = '';
30251         
30252         var canvasEl = document.createElement("canvas");
30253         
30254         var contextEl = canvasEl.getContext("2d");
30255         
30256         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30257         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30258         var center = this.imageEl.OriginWidth / 2;
30259         
30260         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30261             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30262             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30263             center = this.imageEl.OriginHeight / 2;
30264         }
30265         
30266         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30267         
30268         contextEl.translate(center, center);
30269         contextEl.rotate(this.rotate * Math.PI / 180);
30270
30271         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30272         
30273         this.canvasEl = document.createElement("canvas");
30274         
30275         this.contextEl = this.canvasEl.getContext("2d");
30276         
30277         switch (this.rotate) {
30278             case 0 :
30279                 
30280                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30281                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30282                 
30283                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30284                 
30285                 break;
30286             case 90 : 
30287                 
30288                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30289                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30290                 
30291                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30292                     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);
30293                     break;
30294                 }
30295                 
30296                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30297                 
30298                 break;
30299             case 180 :
30300                 
30301                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30302                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30303                 
30304                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30305                     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);
30306                     break;
30307                 }
30308                 
30309                 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);
30310                 
30311                 break;
30312             case 270 :
30313                 
30314                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30315                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30316         
30317                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30318                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30319                     break;
30320                 }
30321                 
30322                 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);
30323                 
30324                 break;
30325             default : 
30326                 break;
30327         }
30328         
30329         this.previewEl.appendChild(this.canvasEl);
30330         
30331         this.setCanvasPosition();
30332     },
30333     
30334     crop : function()
30335     {
30336         if(!this.canvasLoaded){
30337             return;
30338         }
30339         
30340         var imageCanvas = document.createElement("canvas");
30341         
30342         var imageContext = imageCanvas.getContext("2d");
30343         
30344         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30345         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30346         
30347         var center = imageCanvas.width / 2;
30348         
30349         imageContext.translate(center, center);
30350         
30351         imageContext.rotate(this.rotate * Math.PI / 180);
30352         
30353         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30354         
30355         var canvas = document.createElement("canvas");
30356         
30357         var context = canvas.getContext("2d");
30358                 
30359         canvas.width = this.minWidth;
30360         canvas.height = this.minHeight;
30361
30362         switch (this.rotate) {
30363             case 0 :
30364                 
30365                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30366                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30367                 
30368                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30369                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30370                 
30371                 var targetWidth = this.minWidth - 2 * x;
30372                 var targetHeight = this.minHeight - 2 * y;
30373                 
30374                 var scale = 1;
30375                 
30376                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30377                     scale = targetWidth / width;
30378                 }
30379                 
30380                 if(x > 0 && y == 0){
30381                     scale = targetHeight / height;
30382                 }
30383                 
30384                 if(x > 0 && y > 0){
30385                     scale = targetWidth / width;
30386                     
30387                     if(width < height){
30388                         scale = targetHeight / height;
30389                     }
30390                 }
30391                 
30392                 context.scale(scale, scale);
30393                 
30394                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30395                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30396
30397                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30398                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30399
30400                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30401                 
30402                 break;
30403             case 90 : 
30404                 
30405                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30406                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30407                 
30408                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30409                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30410                 
30411                 var targetWidth = this.minWidth - 2 * x;
30412                 var targetHeight = this.minHeight - 2 * y;
30413                 
30414                 var scale = 1;
30415                 
30416                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30417                     scale = targetWidth / width;
30418                 }
30419                 
30420                 if(x > 0 && y == 0){
30421                     scale = targetHeight / height;
30422                 }
30423                 
30424                 if(x > 0 && y > 0){
30425                     scale = targetWidth / width;
30426                     
30427                     if(width < height){
30428                         scale = targetHeight / height;
30429                     }
30430                 }
30431                 
30432                 context.scale(scale, scale);
30433                 
30434                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30435                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30436
30437                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30438                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30439                 
30440                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30441                 
30442                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30443                 
30444                 break;
30445             case 180 :
30446                 
30447                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30448                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30449                 
30450                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30451                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30452                 
30453                 var targetWidth = this.minWidth - 2 * x;
30454                 var targetHeight = this.minHeight - 2 * y;
30455                 
30456                 var scale = 1;
30457                 
30458                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30459                     scale = targetWidth / width;
30460                 }
30461                 
30462                 if(x > 0 && y == 0){
30463                     scale = targetHeight / height;
30464                 }
30465                 
30466                 if(x > 0 && y > 0){
30467                     scale = targetWidth / width;
30468                     
30469                     if(width < height){
30470                         scale = targetHeight / height;
30471                     }
30472                 }
30473                 
30474                 context.scale(scale, scale);
30475                 
30476                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30477                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30478
30479                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30480                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30481
30482                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30483                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30484                 
30485                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30486                 
30487                 break;
30488             case 270 :
30489                 
30490                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30491                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30492                 
30493                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30494                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30495                 
30496                 var targetWidth = this.minWidth - 2 * x;
30497                 var targetHeight = this.minHeight - 2 * y;
30498                 
30499                 var scale = 1;
30500                 
30501                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30502                     scale = targetWidth / width;
30503                 }
30504                 
30505                 if(x > 0 && y == 0){
30506                     scale = targetHeight / height;
30507                 }
30508                 
30509                 if(x > 0 && y > 0){
30510                     scale = targetWidth / width;
30511                     
30512                     if(width < height){
30513                         scale = targetHeight / height;
30514                     }
30515                 }
30516                 
30517                 context.scale(scale, scale);
30518                 
30519                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30520                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30521
30522                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30523                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30524                 
30525                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30526                 
30527                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30528                 
30529                 break;
30530             default : 
30531                 break;
30532         }
30533         
30534         this.cropData = canvas.toDataURL(this.cropType);
30535         
30536         if(this.fireEvent('crop', this, this.cropData) !== false){
30537             this.process(this.file, this.cropData);
30538         }
30539         
30540         return;
30541         
30542     },
30543     
30544     setThumbBoxSize : function()
30545     {
30546         var width, height;
30547         
30548         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30549             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30550             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30551             
30552             this.minWidth = width;
30553             this.minHeight = height;
30554             
30555             if(this.rotate == 90 || this.rotate == 270){
30556                 this.minWidth = height;
30557                 this.minHeight = width;
30558             }
30559         }
30560         
30561         height = 300;
30562         width = Math.ceil(this.minWidth * height / this.minHeight);
30563         
30564         if(this.minWidth > this.minHeight){
30565             width = 300;
30566             height = Math.ceil(this.minHeight * width / this.minWidth);
30567         }
30568         
30569         this.thumbEl.setStyle({
30570             width : width + 'px',
30571             height : height + 'px'
30572         });
30573
30574         return;
30575             
30576     },
30577     
30578     setThumbBoxPosition : function()
30579     {
30580         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30581         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30582         
30583         this.thumbEl.setLeft(x);
30584         this.thumbEl.setTop(y);
30585         
30586     },
30587     
30588     baseRotateLevel : function()
30589     {
30590         this.baseRotate = 1;
30591         
30592         if(
30593                 typeof(this.exif) != 'undefined' &&
30594                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30595                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30596         ){
30597             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30598         }
30599         
30600         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30601         
30602     },
30603     
30604     baseScaleLevel : function()
30605     {
30606         var width, height;
30607         
30608         if(this.isDocument){
30609             
30610             if(this.baseRotate == 6 || this.baseRotate == 8){
30611             
30612                 height = this.thumbEl.getHeight();
30613                 this.baseScale = height / this.imageEl.OriginWidth;
30614
30615                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30616                     width = this.thumbEl.getWidth();
30617                     this.baseScale = width / this.imageEl.OriginHeight;
30618                 }
30619
30620                 return;
30621             }
30622
30623             height = this.thumbEl.getHeight();
30624             this.baseScale = height / this.imageEl.OriginHeight;
30625
30626             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30627                 width = this.thumbEl.getWidth();
30628                 this.baseScale = width / this.imageEl.OriginWidth;
30629             }
30630
30631             return;
30632         }
30633         
30634         if(this.baseRotate == 6 || this.baseRotate == 8){
30635             
30636             width = this.thumbEl.getHeight();
30637             this.baseScale = width / this.imageEl.OriginHeight;
30638             
30639             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30640                 height = this.thumbEl.getWidth();
30641                 this.baseScale = height / this.imageEl.OriginHeight;
30642             }
30643             
30644             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30645                 height = this.thumbEl.getWidth();
30646                 this.baseScale = height / this.imageEl.OriginHeight;
30647                 
30648                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30649                     width = this.thumbEl.getHeight();
30650                     this.baseScale = width / this.imageEl.OriginWidth;
30651                 }
30652             }
30653             
30654             return;
30655         }
30656         
30657         width = this.thumbEl.getWidth();
30658         this.baseScale = width / this.imageEl.OriginWidth;
30659         
30660         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30661             height = this.thumbEl.getHeight();
30662             this.baseScale = height / this.imageEl.OriginHeight;
30663         }
30664         
30665         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30666             
30667             height = this.thumbEl.getHeight();
30668             this.baseScale = height / this.imageEl.OriginHeight;
30669             
30670             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30671                 width = this.thumbEl.getWidth();
30672                 this.baseScale = width / this.imageEl.OriginWidth;
30673             }
30674             
30675         }
30676         
30677         return;
30678     },
30679     
30680     getScaleLevel : function()
30681     {
30682         return this.baseScale * Math.pow(1.1, this.scale);
30683     },
30684     
30685     onTouchStart : function(e)
30686     {
30687         if(!this.canvasLoaded){
30688             this.beforeSelectFile(e);
30689             return;
30690         }
30691         
30692         var touches = e.browserEvent.touches;
30693         
30694         if(!touches){
30695             return;
30696         }
30697         
30698         if(touches.length == 1){
30699             this.onMouseDown(e);
30700             return;
30701         }
30702         
30703         if(touches.length != 2){
30704             return;
30705         }
30706         
30707         var coords = [];
30708         
30709         for(var i = 0, finger; finger = touches[i]; i++){
30710             coords.push(finger.pageX, finger.pageY);
30711         }
30712         
30713         var x = Math.pow(coords[0] - coords[2], 2);
30714         var y = Math.pow(coords[1] - coords[3], 2);
30715         
30716         this.startDistance = Math.sqrt(x + y);
30717         
30718         this.startScale = this.scale;
30719         
30720         this.pinching = true;
30721         this.dragable = false;
30722         
30723     },
30724     
30725     onTouchMove : function(e)
30726     {
30727         if(!this.pinching && !this.dragable){
30728             return;
30729         }
30730         
30731         var touches = e.browserEvent.touches;
30732         
30733         if(!touches){
30734             return;
30735         }
30736         
30737         if(this.dragable){
30738             this.onMouseMove(e);
30739             return;
30740         }
30741         
30742         var coords = [];
30743         
30744         for(var i = 0, finger; finger = touches[i]; i++){
30745             coords.push(finger.pageX, finger.pageY);
30746         }
30747         
30748         var x = Math.pow(coords[0] - coords[2], 2);
30749         var y = Math.pow(coords[1] - coords[3], 2);
30750         
30751         this.endDistance = Math.sqrt(x + y);
30752         
30753         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30754         
30755         if(!this.zoomable()){
30756             this.scale = this.startScale;
30757             return;
30758         }
30759         
30760         this.draw();
30761         
30762     },
30763     
30764     onTouchEnd : function(e)
30765     {
30766         this.pinching = false;
30767         this.dragable = false;
30768         
30769     },
30770     
30771     process : function(file, crop)
30772     {
30773         if(this.loadMask){
30774             this.maskEl.mask(this.loadingText);
30775         }
30776         
30777         this.xhr = new XMLHttpRequest();
30778         
30779         file.xhr = this.xhr;
30780
30781         this.xhr.open(this.method, this.url, true);
30782         
30783         var headers = {
30784             "Accept": "application/json",
30785             "Cache-Control": "no-cache",
30786             "X-Requested-With": "XMLHttpRequest"
30787         };
30788         
30789         for (var headerName in headers) {
30790             var headerValue = headers[headerName];
30791             if (headerValue) {
30792                 this.xhr.setRequestHeader(headerName, headerValue);
30793             }
30794         }
30795         
30796         var _this = this;
30797         
30798         this.xhr.onload = function()
30799         {
30800             _this.xhrOnLoad(_this.xhr);
30801         }
30802         
30803         this.xhr.onerror = function()
30804         {
30805             _this.xhrOnError(_this.xhr);
30806         }
30807         
30808         var formData = new FormData();
30809
30810         formData.append('returnHTML', 'NO');
30811         
30812         if(crop){
30813             formData.append('crop', crop);
30814         }
30815         
30816         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30817             formData.append(this.paramName, file, file.name);
30818         }
30819         
30820         if(typeof(file.filename) != 'undefined'){
30821             formData.append('filename', file.filename);
30822         }
30823         
30824         if(typeof(file.mimetype) != 'undefined'){
30825             formData.append('mimetype', file.mimetype);
30826         }
30827         
30828         if(this.fireEvent('arrange', this, formData) != false){
30829             this.xhr.send(formData);
30830         };
30831     },
30832     
30833     xhrOnLoad : function(xhr)
30834     {
30835         if(this.loadMask){
30836             this.maskEl.unmask();
30837         }
30838         
30839         if (xhr.readyState !== 4) {
30840             this.fireEvent('exception', this, xhr);
30841             return;
30842         }
30843
30844         var response = Roo.decode(xhr.responseText);
30845         
30846         if(!response.success){
30847             this.fireEvent('exception', this, xhr);
30848             return;
30849         }
30850         
30851         var response = Roo.decode(xhr.responseText);
30852         
30853         this.fireEvent('upload', this, response);
30854         
30855     },
30856     
30857     xhrOnError : function()
30858     {
30859         if(this.loadMask){
30860             this.maskEl.unmask();
30861         }
30862         
30863         Roo.log('xhr on error');
30864         
30865         var response = Roo.decode(xhr.responseText);
30866           
30867         Roo.log(response);
30868         
30869     },
30870     
30871     prepare : function(file)
30872     {   
30873         if(this.loadMask){
30874             this.maskEl.mask(this.loadingText);
30875         }
30876         
30877         this.file = false;
30878         this.exif = {};
30879         
30880         if(typeof(file) === 'string'){
30881             this.loadCanvas(file);
30882             return;
30883         }
30884         
30885         if(!file || !this.urlAPI){
30886             return;
30887         }
30888         
30889         this.file = file;
30890         this.cropType = file.type;
30891         
30892         var _this = this;
30893         
30894         if(this.fireEvent('prepare', this, this.file) != false){
30895             
30896             var reader = new FileReader();
30897             
30898             reader.onload = function (e) {
30899                 if (e.target.error) {
30900                     Roo.log(e.target.error);
30901                     return;
30902                 }
30903                 
30904                 var buffer = e.target.result,
30905                     dataView = new DataView(buffer),
30906                     offset = 2,
30907                     maxOffset = dataView.byteLength - 4,
30908                     markerBytes,
30909                     markerLength;
30910                 
30911                 if (dataView.getUint16(0) === 0xffd8) {
30912                     while (offset < maxOffset) {
30913                         markerBytes = dataView.getUint16(offset);
30914                         
30915                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30916                             markerLength = dataView.getUint16(offset + 2) + 2;
30917                             if (offset + markerLength > dataView.byteLength) {
30918                                 Roo.log('Invalid meta data: Invalid segment size.');
30919                                 break;
30920                             }
30921                             
30922                             if(markerBytes == 0xffe1){
30923                                 _this.parseExifData(
30924                                     dataView,
30925                                     offset,
30926                                     markerLength
30927                                 );
30928                             }
30929                             
30930                             offset += markerLength;
30931                             
30932                             continue;
30933                         }
30934                         
30935                         break;
30936                     }
30937                     
30938                 }
30939                 
30940                 var url = _this.urlAPI.createObjectURL(_this.file);
30941                 
30942                 _this.loadCanvas(url);
30943                 
30944                 return;
30945             }
30946             
30947             reader.readAsArrayBuffer(this.file);
30948             
30949         }
30950         
30951     },
30952     
30953     parseExifData : function(dataView, offset, length)
30954     {
30955         var tiffOffset = offset + 10,
30956             littleEndian,
30957             dirOffset;
30958     
30959         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30960             // No Exif data, might be XMP data instead
30961             return;
30962         }
30963         
30964         // Check for the ASCII code for "Exif" (0x45786966):
30965         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30966             // No Exif data, might be XMP data instead
30967             return;
30968         }
30969         if (tiffOffset + 8 > dataView.byteLength) {
30970             Roo.log('Invalid Exif data: Invalid segment size.');
30971             return;
30972         }
30973         // Check for the two null bytes:
30974         if (dataView.getUint16(offset + 8) !== 0x0000) {
30975             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30976             return;
30977         }
30978         // Check the byte alignment:
30979         switch (dataView.getUint16(tiffOffset)) {
30980         case 0x4949:
30981             littleEndian = true;
30982             break;
30983         case 0x4D4D:
30984             littleEndian = false;
30985             break;
30986         default:
30987             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30988             return;
30989         }
30990         // Check for the TIFF tag marker (0x002A):
30991         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30992             Roo.log('Invalid Exif data: Missing TIFF marker.');
30993             return;
30994         }
30995         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30996         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30997         
30998         this.parseExifTags(
30999             dataView,
31000             tiffOffset,
31001             tiffOffset + dirOffset,
31002             littleEndian
31003         );
31004     },
31005     
31006     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31007     {
31008         var tagsNumber,
31009             dirEndOffset,
31010             i;
31011         if (dirOffset + 6 > dataView.byteLength) {
31012             Roo.log('Invalid Exif data: Invalid directory offset.');
31013             return;
31014         }
31015         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31016         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31017         if (dirEndOffset + 4 > dataView.byteLength) {
31018             Roo.log('Invalid Exif data: Invalid directory size.');
31019             return;
31020         }
31021         for (i = 0; i < tagsNumber; i += 1) {
31022             this.parseExifTag(
31023                 dataView,
31024                 tiffOffset,
31025                 dirOffset + 2 + 12 * i, // tag offset
31026                 littleEndian
31027             );
31028         }
31029         // Return the offset to the next directory:
31030         return dataView.getUint32(dirEndOffset, littleEndian);
31031     },
31032     
31033     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31034     {
31035         var tag = dataView.getUint16(offset, littleEndian);
31036         
31037         this.exif[tag] = this.getExifValue(
31038             dataView,
31039             tiffOffset,
31040             offset,
31041             dataView.getUint16(offset + 2, littleEndian), // tag type
31042             dataView.getUint32(offset + 4, littleEndian), // tag length
31043             littleEndian
31044         );
31045     },
31046     
31047     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31048     {
31049         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31050             tagSize,
31051             dataOffset,
31052             values,
31053             i,
31054             str,
31055             c;
31056     
31057         if (!tagType) {
31058             Roo.log('Invalid Exif data: Invalid tag type.');
31059             return;
31060         }
31061         
31062         tagSize = tagType.size * length;
31063         // Determine if the value is contained in the dataOffset bytes,
31064         // or if the value at the dataOffset is a pointer to the actual data:
31065         dataOffset = tagSize > 4 ?
31066                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31067         if (dataOffset + tagSize > dataView.byteLength) {
31068             Roo.log('Invalid Exif data: Invalid data offset.');
31069             return;
31070         }
31071         if (length === 1) {
31072             return tagType.getValue(dataView, dataOffset, littleEndian);
31073         }
31074         values = [];
31075         for (i = 0; i < length; i += 1) {
31076             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31077         }
31078         
31079         if (tagType.ascii) {
31080             str = '';
31081             // Concatenate the chars:
31082             for (i = 0; i < values.length; i += 1) {
31083                 c = values[i];
31084                 // Ignore the terminating NULL byte(s):
31085                 if (c === '\u0000') {
31086                     break;
31087                 }
31088                 str += c;
31089             }
31090             return str;
31091         }
31092         return values;
31093     }
31094     
31095 });
31096
31097 Roo.apply(Roo.bootstrap.UploadCropbox, {
31098     tags : {
31099         'Orientation': 0x0112
31100     },
31101     
31102     Orientation: {
31103             1: 0, //'top-left',
31104 //            2: 'top-right',
31105             3: 180, //'bottom-right',
31106 //            4: 'bottom-left',
31107 //            5: 'left-top',
31108             6: 90, //'right-top',
31109 //            7: 'right-bottom',
31110             8: 270 //'left-bottom'
31111     },
31112     
31113     exifTagTypes : {
31114         // byte, 8-bit unsigned int:
31115         1: {
31116             getValue: function (dataView, dataOffset) {
31117                 return dataView.getUint8(dataOffset);
31118             },
31119             size: 1
31120         },
31121         // ascii, 8-bit byte:
31122         2: {
31123             getValue: function (dataView, dataOffset) {
31124                 return String.fromCharCode(dataView.getUint8(dataOffset));
31125             },
31126             size: 1,
31127             ascii: true
31128         },
31129         // short, 16 bit int:
31130         3: {
31131             getValue: function (dataView, dataOffset, littleEndian) {
31132                 return dataView.getUint16(dataOffset, littleEndian);
31133             },
31134             size: 2
31135         },
31136         // long, 32 bit int:
31137         4: {
31138             getValue: function (dataView, dataOffset, littleEndian) {
31139                 return dataView.getUint32(dataOffset, littleEndian);
31140             },
31141             size: 4
31142         },
31143         // rational = two long values, first is numerator, second is denominator:
31144         5: {
31145             getValue: function (dataView, dataOffset, littleEndian) {
31146                 return dataView.getUint32(dataOffset, littleEndian) /
31147                     dataView.getUint32(dataOffset + 4, littleEndian);
31148             },
31149             size: 8
31150         },
31151         // slong, 32 bit signed int:
31152         9: {
31153             getValue: function (dataView, dataOffset, littleEndian) {
31154                 return dataView.getInt32(dataOffset, littleEndian);
31155             },
31156             size: 4
31157         },
31158         // srational, two slongs, first is numerator, second is denominator:
31159         10: {
31160             getValue: function (dataView, dataOffset, littleEndian) {
31161                 return dataView.getInt32(dataOffset, littleEndian) /
31162                     dataView.getInt32(dataOffset + 4, littleEndian);
31163             },
31164             size: 8
31165         }
31166     },
31167     
31168     footer : {
31169         STANDARD : [
31170             {
31171                 tag : 'div',
31172                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31173                 action : 'rotate-left',
31174                 cn : [
31175                     {
31176                         tag : 'button',
31177                         cls : 'btn btn-default',
31178                         html : '<i class="fa fa-undo"></i>'
31179                     }
31180                 ]
31181             },
31182             {
31183                 tag : 'div',
31184                 cls : 'btn-group roo-upload-cropbox-picture',
31185                 action : 'picture',
31186                 cn : [
31187                     {
31188                         tag : 'button',
31189                         cls : 'btn btn-default',
31190                         html : '<i class="fa fa-picture-o"></i>'
31191                     }
31192                 ]
31193             },
31194             {
31195                 tag : 'div',
31196                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31197                 action : 'rotate-right',
31198                 cn : [
31199                     {
31200                         tag : 'button',
31201                         cls : 'btn btn-default',
31202                         html : '<i class="fa fa-repeat"></i>'
31203                     }
31204                 ]
31205             }
31206         ],
31207         DOCUMENT : [
31208             {
31209                 tag : 'div',
31210                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31211                 action : 'rotate-left',
31212                 cn : [
31213                     {
31214                         tag : 'button',
31215                         cls : 'btn btn-default',
31216                         html : '<i class="fa fa-undo"></i>'
31217                     }
31218                 ]
31219             },
31220             {
31221                 tag : 'div',
31222                 cls : 'btn-group roo-upload-cropbox-download',
31223                 action : 'download',
31224                 cn : [
31225                     {
31226                         tag : 'button',
31227                         cls : 'btn btn-default',
31228                         html : '<i class="fa fa-download"></i>'
31229                     }
31230                 ]
31231             },
31232             {
31233                 tag : 'div',
31234                 cls : 'btn-group roo-upload-cropbox-crop',
31235                 action : 'crop',
31236                 cn : [
31237                     {
31238                         tag : 'button',
31239                         cls : 'btn btn-default',
31240                         html : '<i class="fa fa-crop"></i>'
31241                     }
31242                 ]
31243             },
31244             {
31245                 tag : 'div',
31246                 cls : 'btn-group roo-upload-cropbox-trash',
31247                 action : 'trash',
31248                 cn : [
31249                     {
31250                         tag : 'button',
31251                         cls : 'btn btn-default',
31252                         html : '<i class="fa fa-trash"></i>'
31253                     }
31254                 ]
31255             },
31256             {
31257                 tag : 'div',
31258                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31259                 action : 'rotate-right',
31260                 cn : [
31261                     {
31262                         tag : 'button',
31263                         cls : 'btn btn-default',
31264                         html : '<i class="fa fa-repeat"></i>'
31265                     }
31266                 ]
31267             }
31268         ],
31269         ROTATOR : [
31270             {
31271                 tag : 'div',
31272                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31273                 action : 'rotate-left',
31274                 cn : [
31275                     {
31276                         tag : 'button',
31277                         cls : 'btn btn-default',
31278                         html : '<i class="fa fa-undo"></i>'
31279                     }
31280                 ]
31281             },
31282             {
31283                 tag : 'div',
31284                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31285                 action : 'rotate-right',
31286                 cn : [
31287                     {
31288                         tag : 'button',
31289                         cls : 'btn btn-default',
31290                         html : '<i class="fa fa-repeat"></i>'
31291                     }
31292                 ]
31293             }
31294         ]
31295     }
31296 });
31297
31298 /*
31299 * Licence: LGPL
31300 */
31301
31302 /**
31303  * @class Roo.bootstrap.DocumentManager
31304  * @extends Roo.bootstrap.Component
31305  * Bootstrap DocumentManager class
31306  * @cfg {String} paramName default 'imageUpload'
31307  * @cfg {String} toolTipName default 'filename'
31308  * @cfg {String} method default POST
31309  * @cfg {String} url action url
31310  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31311  * @cfg {Boolean} multiple multiple upload default true
31312  * @cfg {Number} thumbSize default 300
31313  * @cfg {String} fieldLabel
31314  * @cfg {Number} labelWidth default 4
31315  * @cfg {String} labelAlign (left|top) default left
31316  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31317 * @cfg {Number} labellg set the width of label (1-12)
31318  * @cfg {Number} labelmd set the width of label (1-12)
31319  * @cfg {Number} labelsm set the width of label (1-12)
31320  * @cfg {Number} labelxs set the width of label (1-12)
31321  * 
31322  * @constructor
31323  * Create a new DocumentManager
31324  * @param {Object} config The config object
31325  */
31326
31327 Roo.bootstrap.DocumentManager = function(config){
31328     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31329     
31330     this.files = [];
31331     this.delegates = [];
31332     
31333     this.addEvents({
31334         /**
31335          * @event initial
31336          * Fire when initial the DocumentManager
31337          * @param {Roo.bootstrap.DocumentManager} this
31338          */
31339         "initial" : true,
31340         /**
31341          * @event inspect
31342          * inspect selected file
31343          * @param {Roo.bootstrap.DocumentManager} this
31344          * @param {File} file
31345          */
31346         "inspect" : true,
31347         /**
31348          * @event exception
31349          * Fire when xhr load exception
31350          * @param {Roo.bootstrap.DocumentManager} this
31351          * @param {XMLHttpRequest} xhr
31352          */
31353         "exception" : true,
31354         /**
31355          * @event afterupload
31356          * Fire when xhr load exception
31357          * @param {Roo.bootstrap.DocumentManager} this
31358          * @param {XMLHttpRequest} xhr
31359          */
31360         "afterupload" : true,
31361         /**
31362          * @event prepare
31363          * prepare the form data
31364          * @param {Roo.bootstrap.DocumentManager} this
31365          * @param {Object} formData
31366          */
31367         "prepare" : true,
31368         /**
31369          * @event remove
31370          * Fire when remove the file
31371          * @param {Roo.bootstrap.DocumentManager} this
31372          * @param {Object} file
31373          */
31374         "remove" : true,
31375         /**
31376          * @event refresh
31377          * Fire after refresh the file
31378          * @param {Roo.bootstrap.DocumentManager} this
31379          */
31380         "refresh" : true,
31381         /**
31382          * @event click
31383          * Fire after click the image
31384          * @param {Roo.bootstrap.DocumentManager} this
31385          * @param {Object} file
31386          */
31387         "click" : true,
31388         /**
31389          * @event edit
31390          * Fire when upload a image and editable set to true
31391          * @param {Roo.bootstrap.DocumentManager} this
31392          * @param {Object} file
31393          */
31394         "edit" : true,
31395         /**
31396          * @event beforeselectfile
31397          * Fire before select file
31398          * @param {Roo.bootstrap.DocumentManager} this
31399          */
31400         "beforeselectfile" : true,
31401         /**
31402          * @event process
31403          * Fire before process file
31404          * @param {Roo.bootstrap.DocumentManager} this
31405          * @param {Object} file
31406          */
31407         "process" : true,
31408         /**
31409          * @event previewrendered
31410          * Fire when preview rendered
31411          * @param {Roo.bootstrap.DocumentManager} this
31412          * @param {Object} file
31413          */
31414         "previewrendered" : true,
31415         /**
31416          */
31417         "previewResize" : true
31418         
31419     });
31420 };
31421
31422 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31423     
31424     boxes : 0,
31425     inputName : '',
31426     thumbSize : 300,
31427     multiple : true,
31428     files : false,
31429     method : 'POST',
31430     url : '',
31431     paramName : 'imageUpload',
31432     toolTipName : 'filename',
31433     fieldLabel : '',
31434     labelWidth : 4,
31435     labelAlign : 'left',
31436     editable : true,
31437     delegates : false,
31438     xhr : false, 
31439     
31440     labellg : 0,
31441     labelmd : 0,
31442     labelsm : 0,
31443     labelxs : 0,
31444     
31445     getAutoCreate : function()
31446     {   
31447         var managerWidget = {
31448             tag : 'div',
31449             cls : 'roo-document-manager',
31450             cn : [
31451                 {
31452                     tag : 'input',
31453                     cls : 'roo-document-manager-selector',
31454                     type : 'file'
31455                 },
31456                 {
31457                     tag : 'div',
31458                     cls : 'roo-document-manager-uploader',
31459                     cn : [
31460                         {
31461                             tag : 'div',
31462                             cls : 'roo-document-manager-upload-btn',
31463                             html : '<i class="fa fa-plus"></i>'
31464                         }
31465                     ]
31466                     
31467                 }
31468             ]
31469         };
31470         
31471         var content = [
31472             {
31473                 tag : 'div',
31474                 cls : 'column col-md-12',
31475                 cn : managerWidget
31476             }
31477         ];
31478         
31479         if(this.fieldLabel.length){
31480             
31481             content = [
31482                 {
31483                     tag : 'div',
31484                     cls : 'column col-md-12',
31485                     html : this.fieldLabel
31486                 },
31487                 {
31488                     tag : 'div',
31489                     cls : 'column col-md-12',
31490                     cn : managerWidget
31491                 }
31492             ];
31493
31494             if(this.labelAlign == 'left'){
31495                 content = [
31496                     {
31497                         tag : 'div',
31498                         cls : 'column',
31499                         html : this.fieldLabel
31500                     },
31501                     {
31502                         tag : 'div',
31503                         cls : 'column',
31504                         cn : managerWidget
31505                     }
31506                 ];
31507                 
31508                 if(this.labelWidth > 12){
31509                     content[0].style = "width: " + this.labelWidth + 'px';
31510                 }
31511
31512                 if(this.labelWidth < 13 && this.labelmd == 0){
31513                     this.labelmd = this.labelWidth;
31514                 }
31515
31516                 if(this.labellg > 0){
31517                     content[0].cls += ' col-lg-' + this.labellg;
31518                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31519                 }
31520
31521                 if(this.labelmd > 0){
31522                     content[0].cls += ' col-md-' + this.labelmd;
31523                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31524                 }
31525
31526                 if(this.labelsm > 0){
31527                     content[0].cls += ' col-sm-' + this.labelsm;
31528                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31529                 }
31530
31531                 if(this.labelxs > 0){
31532                     content[0].cls += ' col-xs-' + this.labelxs;
31533                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31534                 }
31535                 
31536             }
31537         }
31538         
31539         var cfg = {
31540             tag : 'div',
31541             cls : 'row clearfix',
31542             cn : content
31543         };
31544         
31545         return cfg;
31546         
31547     },
31548     
31549     initEvents : function()
31550     {
31551         this.managerEl = this.el.select('.roo-document-manager', true).first();
31552         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31553         
31554         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31555         this.selectorEl.hide();
31556         
31557         if(this.multiple){
31558             this.selectorEl.attr('multiple', 'multiple');
31559         }
31560         
31561         this.selectorEl.on('change', this.onFileSelected, this);
31562         
31563         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31564         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31565         
31566         this.uploader.on('click', this.onUploaderClick, this);
31567         
31568         this.renderProgressDialog();
31569         
31570         var _this = this;
31571         
31572         window.addEventListener("resize", function() { _this.refresh(); } );
31573         
31574         this.fireEvent('initial', this);
31575     },
31576     
31577     renderProgressDialog : function()
31578     {
31579         var _this = this;
31580         
31581         this.progressDialog = new Roo.bootstrap.Modal({
31582             cls : 'roo-document-manager-progress-dialog',
31583             allow_close : false,
31584             animate : false,
31585             title : '',
31586             buttons : [
31587                 {
31588                     name  :'cancel',
31589                     weight : 'danger',
31590                     html : 'Cancel'
31591                 }
31592             ], 
31593             listeners : { 
31594                 btnclick : function() {
31595                     _this.uploadCancel();
31596                     this.hide();
31597                 }
31598             }
31599         });
31600          
31601         this.progressDialog.render(Roo.get(document.body));
31602          
31603         this.progress = new Roo.bootstrap.Progress({
31604             cls : 'roo-document-manager-progress',
31605             active : true,
31606             striped : true
31607         });
31608         
31609         this.progress.render(this.progressDialog.getChildContainer());
31610         
31611         this.progressBar = new Roo.bootstrap.ProgressBar({
31612             cls : 'roo-document-manager-progress-bar',
31613             aria_valuenow : 0,
31614             aria_valuemin : 0,
31615             aria_valuemax : 12,
31616             panel : 'success'
31617         });
31618         
31619         this.progressBar.render(this.progress.getChildContainer());
31620     },
31621     
31622     onUploaderClick : function(e)
31623     {
31624         e.preventDefault();
31625      
31626         if(this.fireEvent('beforeselectfile', this) != false){
31627             this.selectorEl.dom.click();
31628         }
31629         
31630     },
31631     
31632     onFileSelected : function(e)
31633     {
31634         e.preventDefault();
31635         
31636         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31637             return;
31638         }
31639         
31640         Roo.each(this.selectorEl.dom.files, function(file){
31641             if(this.fireEvent('inspect', this, file) != false){
31642                 this.files.push(file);
31643             }
31644         }, this);
31645         
31646         this.queue();
31647         
31648     },
31649     
31650     queue : function()
31651     {
31652         this.selectorEl.dom.value = '';
31653         
31654         if(!this.files || !this.files.length){
31655             return;
31656         }
31657         
31658         if(this.boxes > 0 && this.files.length > this.boxes){
31659             this.files = this.files.slice(0, this.boxes);
31660         }
31661         
31662         this.uploader.show();
31663         
31664         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31665             this.uploader.hide();
31666         }
31667         
31668         var _this = this;
31669         
31670         var files = [];
31671         
31672         var docs = [];
31673         
31674         Roo.each(this.files, function(file){
31675             
31676             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31677                 var f = this.renderPreview(file);
31678                 files.push(f);
31679                 return;
31680             }
31681             
31682             if(file.type.indexOf('image') != -1){
31683                 this.delegates.push(
31684                     (function(){
31685                         _this.process(file);
31686                     }).createDelegate(this)
31687                 );
31688         
31689                 return;
31690             }
31691             
31692             docs.push(
31693                 (function(){
31694                     _this.process(file);
31695                 }).createDelegate(this)
31696             );
31697             
31698         }, this);
31699         
31700         this.files = files;
31701         
31702         this.delegates = this.delegates.concat(docs);
31703         
31704         if(!this.delegates.length){
31705             this.refresh();
31706             return;
31707         }
31708         
31709         this.progressBar.aria_valuemax = this.delegates.length;
31710         
31711         this.arrange();
31712         
31713         return;
31714     },
31715     
31716     arrange : function()
31717     {
31718         if(!this.delegates.length){
31719             this.progressDialog.hide();
31720             this.refresh();
31721             return;
31722         }
31723         
31724         var delegate = this.delegates.shift();
31725         
31726         this.progressDialog.show();
31727         
31728         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31729         
31730         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31731         
31732         delegate();
31733     },
31734     
31735     refresh : function()
31736     {
31737         this.uploader.show();
31738         
31739         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31740             this.uploader.hide();
31741         }
31742         
31743         Roo.isTouch ? this.closable(false) : this.closable(true);
31744         
31745         this.fireEvent('refresh', this);
31746     },
31747     
31748     onRemove : function(e, el, o)
31749     {
31750         e.preventDefault();
31751         
31752         this.fireEvent('remove', this, o);
31753         
31754     },
31755     
31756     remove : function(o)
31757     {
31758         var files = [];
31759         
31760         Roo.each(this.files, function(file){
31761             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31762                 files.push(file);
31763                 return;
31764             }
31765
31766             o.target.remove();
31767
31768         }, this);
31769         
31770         this.files = files;
31771         
31772         this.refresh();
31773     },
31774     
31775     clear : function()
31776     {
31777         Roo.each(this.files, function(file){
31778             if(!file.target){
31779                 return;
31780             }
31781             
31782             file.target.remove();
31783
31784         }, this);
31785         
31786         this.files = [];
31787         
31788         this.refresh();
31789     },
31790     
31791     onClick : function(e, el, o)
31792     {
31793         e.preventDefault();
31794         
31795         this.fireEvent('click', this, o);
31796         
31797     },
31798     
31799     closable : function(closable)
31800     {
31801         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31802             
31803             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31804             
31805             if(closable){
31806                 el.show();
31807                 return;
31808             }
31809             
31810             el.hide();
31811             
31812         }, this);
31813     },
31814     
31815     xhrOnLoad : function(xhr)
31816     {
31817         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31818             el.remove();
31819         }, this);
31820         
31821         if (xhr.readyState !== 4) {
31822             this.arrange();
31823             this.fireEvent('exception', this, xhr);
31824             return;
31825         }
31826
31827         var response = Roo.decode(xhr.responseText);
31828         
31829         if(!response.success){
31830             this.arrange();
31831             this.fireEvent('exception', this, xhr);
31832             return;
31833         }
31834         
31835         var file = this.renderPreview(response.data);
31836         
31837         this.files.push(file);
31838         
31839         this.arrange();
31840         
31841         this.fireEvent('afterupload', this, xhr);
31842         
31843     },
31844     
31845     xhrOnError : function(xhr)
31846     {
31847         Roo.log('xhr on error');
31848         
31849         var response = Roo.decode(xhr.responseText);
31850           
31851         Roo.log(response);
31852         
31853         this.arrange();
31854     },
31855     
31856     process : function(file)
31857     {
31858         if(this.fireEvent('process', this, file) !== false){
31859             if(this.editable && file.type.indexOf('image') != -1){
31860                 this.fireEvent('edit', this, file);
31861                 return;
31862             }
31863
31864             this.uploadStart(file, false);
31865
31866             return;
31867         }
31868         
31869     },
31870     
31871     uploadStart : function(file, crop)
31872     {
31873         this.xhr = new XMLHttpRequest();
31874         
31875         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31876             this.arrange();
31877             return;
31878         }
31879         
31880         file.xhr = this.xhr;
31881             
31882         this.managerEl.createChild({
31883             tag : 'div',
31884             cls : 'roo-document-manager-loading',
31885             cn : [
31886                 {
31887                     tag : 'div',
31888                     tooltip : file.name,
31889                     cls : 'roo-document-manager-thumb',
31890                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31891                 }
31892             ]
31893
31894         });
31895
31896         this.xhr.open(this.method, this.url, true);
31897         
31898         var headers = {
31899             "Accept": "application/json",
31900             "Cache-Control": "no-cache",
31901             "X-Requested-With": "XMLHttpRequest"
31902         };
31903         
31904         for (var headerName in headers) {
31905             var headerValue = headers[headerName];
31906             if (headerValue) {
31907                 this.xhr.setRequestHeader(headerName, headerValue);
31908             }
31909         }
31910         
31911         var _this = this;
31912         
31913         this.xhr.onload = function()
31914         {
31915             _this.xhrOnLoad(_this.xhr);
31916         }
31917         
31918         this.xhr.onerror = function()
31919         {
31920             _this.xhrOnError(_this.xhr);
31921         }
31922         
31923         var formData = new FormData();
31924
31925         formData.append('returnHTML', 'NO');
31926         
31927         if(crop){
31928             formData.append('crop', crop);
31929         }
31930         
31931         formData.append(this.paramName, file, file.name);
31932         
31933         var options = {
31934             file : file, 
31935             manually : false
31936         };
31937         
31938         if(this.fireEvent('prepare', this, formData, options) != false){
31939             
31940             if(options.manually){
31941                 return;
31942             }
31943             
31944             this.xhr.send(formData);
31945             return;
31946         };
31947         
31948         this.uploadCancel();
31949     },
31950     
31951     uploadCancel : function()
31952     {
31953         if (this.xhr) {
31954             this.xhr.abort();
31955         }
31956         
31957         this.delegates = [];
31958         
31959         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31960             el.remove();
31961         }, this);
31962         
31963         this.arrange();
31964     },
31965     
31966     renderPreview : function(file)
31967     {
31968         if(typeof(file.target) != 'undefined' && file.target){
31969             return file;
31970         }
31971         
31972         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31973         
31974         var previewEl = this.managerEl.createChild({
31975             tag : 'div',
31976             cls : 'roo-document-manager-preview',
31977             cn : [
31978                 {
31979                     tag : 'div',
31980                     tooltip : file[this.toolTipName],
31981                     cls : 'roo-document-manager-thumb',
31982                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31983                 },
31984                 {
31985                     tag : 'button',
31986                     cls : 'close',
31987                     html : '<i class="fa fa-times-circle"></i>'
31988                 }
31989             ]
31990         });
31991
31992         var close = previewEl.select('button.close', true).first();
31993
31994         close.on('click', this.onRemove, this, file);
31995
31996         file.target = previewEl;
31997
31998         var image = previewEl.select('img', true).first();
31999         
32000         var _this = this;
32001         
32002         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32003         
32004         image.on('click', this.onClick, this, file);
32005         
32006         this.fireEvent('previewrendered', this, file);
32007         
32008         return file;
32009         
32010     },
32011     
32012     onPreviewLoad : function(file, image)
32013     {
32014         if(typeof(file.target) == 'undefined' || !file.target){
32015             return;
32016         }
32017         
32018         var width = image.dom.naturalWidth || image.dom.width;
32019         var height = image.dom.naturalHeight || image.dom.height;
32020         
32021         if(!this.previewResize) {
32022             return;
32023         }
32024         
32025         if(width > height){
32026             file.target.addClass('wide');
32027             return;
32028         }
32029         
32030         file.target.addClass('tall');
32031         return;
32032         
32033     },
32034     
32035     uploadFromSource : function(file, crop)
32036     {
32037         this.xhr = new XMLHttpRequest();
32038         
32039         this.managerEl.createChild({
32040             tag : 'div',
32041             cls : 'roo-document-manager-loading',
32042             cn : [
32043                 {
32044                     tag : 'div',
32045                     tooltip : file.name,
32046                     cls : 'roo-document-manager-thumb',
32047                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32048                 }
32049             ]
32050
32051         });
32052
32053         this.xhr.open(this.method, this.url, true);
32054         
32055         var headers = {
32056             "Accept": "application/json",
32057             "Cache-Control": "no-cache",
32058             "X-Requested-With": "XMLHttpRequest"
32059         };
32060         
32061         for (var headerName in headers) {
32062             var headerValue = headers[headerName];
32063             if (headerValue) {
32064                 this.xhr.setRequestHeader(headerName, headerValue);
32065             }
32066         }
32067         
32068         var _this = this;
32069         
32070         this.xhr.onload = function()
32071         {
32072             _this.xhrOnLoad(_this.xhr);
32073         }
32074         
32075         this.xhr.onerror = function()
32076         {
32077             _this.xhrOnError(_this.xhr);
32078         }
32079         
32080         var formData = new FormData();
32081
32082         formData.append('returnHTML', 'NO');
32083         
32084         formData.append('crop', crop);
32085         
32086         if(typeof(file.filename) != 'undefined'){
32087             formData.append('filename', file.filename);
32088         }
32089         
32090         if(typeof(file.mimetype) != 'undefined'){
32091             formData.append('mimetype', file.mimetype);
32092         }
32093         
32094         Roo.log(formData);
32095         
32096         if(this.fireEvent('prepare', this, formData) != false){
32097             this.xhr.send(formData);
32098         };
32099     }
32100 });
32101
32102 /*
32103 * Licence: LGPL
32104 */
32105
32106 /**
32107  * @class Roo.bootstrap.DocumentViewer
32108  * @extends Roo.bootstrap.Component
32109  * Bootstrap DocumentViewer class
32110  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32111  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32112  * 
32113  * @constructor
32114  * Create a new DocumentViewer
32115  * @param {Object} config The config object
32116  */
32117
32118 Roo.bootstrap.DocumentViewer = function(config){
32119     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32120     
32121     this.addEvents({
32122         /**
32123          * @event initial
32124          * Fire after initEvent
32125          * @param {Roo.bootstrap.DocumentViewer} this
32126          */
32127         "initial" : true,
32128         /**
32129          * @event click
32130          * Fire after click
32131          * @param {Roo.bootstrap.DocumentViewer} this
32132          */
32133         "click" : true,
32134         /**
32135          * @event download
32136          * Fire after download button
32137          * @param {Roo.bootstrap.DocumentViewer} this
32138          */
32139         "download" : true,
32140         /**
32141          * @event trash
32142          * Fire after trash button
32143          * @param {Roo.bootstrap.DocumentViewer} this
32144          */
32145         "trash" : true
32146         
32147     });
32148 };
32149
32150 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32151     
32152     showDownload : true,
32153     
32154     showTrash : true,
32155     
32156     getAutoCreate : function()
32157     {
32158         var cfg = {
32159             tag : 'div',
32160             cls : 'roo-document-viewer',
32161             cn : [
32162                 {
32163                     tag : 'div',
32164                     cls : 'roo-document-viewer-body',
32165                     cn : [
32166                         {
32167                             tag : 'div',
32168                             cls : 'roo-document-viewer-thumb',
32169                             cn : [
32170                                 {
32171                                     tag : 'img',
32172                                     cls : 'roo-document-viewer-image'
32173                                 }
32174                             ]
32175                         }
32176                     ]
32177                 },
32178                 {
32179                     tag : 'div',
32180                     cls : 'roo-document-viewer-footer',
32181                     cn : {
32182                         tag : 'div',
32183                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32184                         cn : [
32185                             {
32186                                 tag : 'div',
32187                                 cls : 'btn-group roo-document-viewer-download',
32188                                 cn : [
32189                                     {
32190                                         tag : 'button',
32191                                         cls : 'btn btn-default',
32192                                         html : '<i class="fa fa-download"></i>'
32193                                     }
32194                                 ]
32195                             },
32196                             {
32197                                 tag : 'div',
32198                                 cls : 'btn-group roo-document-viewer-trash',
32199                                 cn : [
32200                                     {
32201                                         tag : 'button',
32202                                         cls : 'btn btn-default',
32203                                         html : '<i class="fa fa-trash"></i>'
32204                                     }
32205                                 ]
32206                             }
32207                         ]
32208                     }
32209                 }
32210             ]
32211         };
32212         
32213         return cfg;
32214     },
32215     
32216     initEvents : function()
32217     {
32218         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32219         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32220         
32221         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32222         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32223         
32224         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32225         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32226         
32227         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32228         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32229         
32230         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32231         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32232         
32233         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32234         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32235         
32236         this.bodyEl.on('click', this.onClick, this);
32237         this.downloadBtn.on('click', this.onDownload, this);
32238         this.trashBtn.on('click', this.onTrash, this);
32239         
32240         this.downloadBtn.hide();
32241         this.trashBtn.hide();
32242         
32243         if(this.showDownload){
32244             this.downloadBtn.show();
32245         }
32246         
32247         if(this.showTrash){
32248             this.trashBtn.show();
32249         }
32250         
32251         if(!this.showDownload && !this.showTrash) {
32252             this.footerEl.hide();
32253         }
32254         
32255     },
32256     
32257     initial : function()
32258     {
32259         this.fireEvent('initial', this);
32260         
32261     },
32262     
32263     onClick : function(e)
32264     {
32265         e.preventDefault();
32266         
32267         this.fireEvent('click', this);
32268     },
32269     
32270     onDownload : function(e)
32271     {
32272         e.preventDefault();
32273         
32274         this.fireEvent('download', this);
32275     },
32276     
32277     onTrash : function(e)
32278     {
32279         e.preventDefault();
32280         
32281         this.fireEvent('trash', this);
32282     }
32283     
32284 });
32285 /*
32286  * - LGPL
32287  *
32288  * nav progress bar
32289  * 
32290  */
32291
32292 /**
32293  * @class Roo.bootstrap.NavProgressBar
32294  * @extends Roo.bootstrap.Component
32295  * Bootstrap NavProgressBar class
32296  * 
32297  * @constructor
32298  * Create a new nav progress bar
32299  * @param {Object} config The config object
32300  */
32301
32302 Roo.bootstrap.NavProgressBar = function(config){
32303     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32304
32305     this.bullets = this.bullets || [];
32306    
32307 //    Roo.bootstrap.NavProgressBar.register(this);
32308      this.addEvents({
32309         /**
32310              * @event changed
32311              * Fires when the active item changes
32312              * @param {Roo.bootstrap.NavProgressBar} this
32313              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32314              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32315          */
32316         'changed': true
32317      });
32318     
32319 };
32320
32321 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32322     
32323     bullets : [],
32324     barItems : [],
32325     
32326     getAutoCreate : function()
32327     {
32328         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32329         
32330         cfg = {
32331             tag : 'div',
32332             cls : 'roo-navigation-bar-group',
32333             cn : [
32334                 {
32335                     tag : 'div',
32336                     cls : 'roo-navigation-top-bar'
32337                 },
32338                 {
32339                     tag : 'div',
32340                     cls : 'roo-navigation-bullets-bar',
32341                     cn : [
32342                         {
32343                             tag : 'ul',
32344                             cls : 'roo-navigation-bar'
32345                         }
32346                     ]
32347                 },
32348                 
32349                 {
32350                     tag : 'div',
32351                     cls : 'roo-navigation-bottom-bar'
32352                 }
32353             ]
32354             
32355         };
32356         
32357         return cfg;
32358         
32359     },
32360     
32361     initEvents: function() 
32362     {
32363         
32364     },
32365     
32366     onRender : function(ct, position) 
32367     {
32368         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32369         
32370         if(this.bullets.length){
32371             Roo.each(this.bullets, function(b){
32372                this.addItem(b);
32373             }, this);
32374         }
32375         
32376         this.format();
32377         
32378     },
32379     
32380     addItem : function(cfg)
32381     {
32382         var item = new Roo.bootstrap.NavProgressItem(cfg);
32383         
32384         item.parentId = this.id;
32385         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32386         
32387         if(cfg.html){
32388             var top = new Roo.bootstrap.Element({
32389                 tag : 'div',
32390                 cls : 'roo-navigation-bar-text'
32391             });
32392             
32393             var bottom = new Roo.bootstrap.Element({
32394                 tag : 'div',
32395                 cls : 'roo-navigation-bar-text'
32396             });
32397             
32398             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32399             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32400             
32401             var topText = new Roo.bootstrap.Element({
32402                 tag : 'span',
32403                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32404             });
32405             
32406             var bottomText = new Roo.bootstrap.Element({
32407                 tag : 'span',
32408                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32409             });
32410             
32411             topText.onRender(top.el, null);
32412             bottomText.onRender(bottom.el, null);
32413             
32414             item.topEl = top;
32415             item.bottomEl = bottom;
32416         }
32417         
32418         this.barItems.push(item);
32419         
32420         return item;
32421     },
32422     
32423     getActive : function()
32424     {
32425         var active = false;
32426         
32427         Roo.each(this.barItems, function(v){
32428             
32429             if (!v.isActive()) {
32430                 return;
32431             }
32432             
32433             active = v;
32434             return false;
32435             
32436         });
32437         
32438         return active;
32439     },
32440     
32441     setActiveItem : function(item)
32442     {
32443         var prev = false;
32444         
32445         Roo.each(this.barItems, function(v){
32446             if (v.rid == item.rid) {
32447                 return ;
32448             }
32449             
32450             if (v.isActive()) {
32451                 v.setActive(false);
32452                 prev = v;
32453             }
32454         });
32455
32456         item.setActive(true);
32457         
32458         this.fireEvent('changed', this, item, prev);
32459     },
32460     
32461     getBarItem: function(rid)
32462     {
32463         var ret = false;
32464         
32465         Roo.each(this.barItems, function(e) {
32466             if (e.rid != rid) {
32467                 return;
32468             }
32469             
32470             ret =  e;
32471             return false;
32472         });
32473         
32474         return ret;
32475     },
32476     
32477     indexOfItem : function(item)
32478     {
32479         var index = false;
32480         
32481         Roo.each(this.barItems, function(v, i){
32482             
32483             if (v.rid != item.rid) {
32484                 return;
32485             }
32486             
32487             index = i;
32488             return false
32489         });
32490         
32491         return index;
32492     },
32493     
32494     setActiveNext : function()
32495     {
32496         var i = this.indexOfItem(this.getActive());
32497         
32498         if (i > this.barItems.length) {
32499             return;
32500         }
32501         
32502         this.setActiveItem(this.barItems[i+1]);
32503     },
32504     
32505     setActivePrev : function()
32506     {
32507         var i = this.indexOfItem(this.getActive());
32508         
32509         if (i  < 1) {
32510             return;
32511         }
32512         
32513         this.setActiveItem(this.barItems[i-1]);
32514     },
32515     
32516     format : function()
32517     {
32518         if(!this.barItems.length){
32519             return;
32520         }
32521      
32522         var width = 100 / this.barItems.length;
32523         
32524         Roo.each(this.barItems, function(i){
32525             i.el.setStyle('width', width + '%');
32526             i.topEl.el.setStyle('width', width + '%');
32527             i.bottomEl.el.setStyle('width', width + '%');
32528         }, this);
32529         
32530     }
32531     
32532 });
32533 /*
32534  * - LGPL
32535  *
32536  * Nav Progress Item
32537  * 
32538  */
32539
32540 /**
32541  * @class Roo.bootstrap.NavProgressItem
32542  * @extends Roo.bootstrap.Component
32543  * Bootstrap NavProgressItem class
32544  * @cfg {String} rid the reference id
32545  * @cfg {Boolean} active (true|false) Is item active default false
32546  * @cfg {Boolean} disabled (true|false) Is item active default false
32547  * @cfg {String} html
32548  * @cfg {String} position (top|bottom) text position default bottom
32549  * @cfg {String} icon show icon instead of number
32550  * 
32551  * @constructor
32552  * Create a new NavProgressItem
32553  * @param {Object} config The config object
32554  */
32555 Roo.bootstrap.NavProgressItem = function(config){
32556     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32557     this.addEvents({
32558         // raw events
32559         /**
32560          * @event click
32561          * The raw click event for the entire grid.
32562          * @param {Roo.bootstrap.NavProgressItem} this
32563          * @param {Roo.EventObject} e
32564          */
32565         "click" : true
32566     });
32567    
32568 };
32569
32570 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32571     
32572     rid : '',
32573     active : false,
32574     disabled : false,
32575     html : '',
32576     position : 'bottom',
32577     icon : false,
32578     
32579     getAutoCreate : function()
32580     {
32581         var iconCls = 'roo-navigation-bar-item-icon';
32582         
32583         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32584         
32585         var cfg = {
32586             tag: 'li',
32587             cls: 'roo-navigation-bar-item',
32588             cn : [
32589                 {
32590                     tag : 'i',
32591                     cls : iconCls
32592                 }
32593             ]
32594         };
32595         
32596         if(this.active){
32597             cfg.cls += ' active';
32598         }
32599         if(this.disabled){
32600             cfg.cls += ' disabled';
32601         }
32602         
32603         return cfg;
32604     },
32605     
32606     disable : function()
32607     {
32608         this.setDisabled(true);
32609     },
32610     
32611     enable : function()
32612     {
32613         this.setDisabled(false);
32614     },
32615     
32616     initEvents: function() 
32617     {
32618         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32619         
32620         this.iconEl.on('click', this.onClick, this);
32621     },
32622     
32623     onClick : function(e)
32624     {
32625         e.preventDefault();
32626         
32627         if(this.disabled){
32628             return;
32629         }
32630         
32631         if(this.fireEvent('click', this, e) === false){
32632             return;
32633         };
32634         
32635         this.parent().setActiveItem(this);
32636     },
32637     
32638     isActive: function () 
32639     {
32640         return this.active;
32641     },
32642     
32643     setActive : function(state)
32644     {
32645         if(this.active == state){
32646             return;
32647         }
32648         
32649         this.active = state;
32650         
32651         if (state) {
32652             this.el.addClass('active');
32653             return;
32654         }
32655         
32656         this.el.removeClass('active');
32657         
32658         return;
32659     },
32660     
32661     setDisabled : function(state)
32662     {
32663         if(this.disabled == state){
32664             return;
32665         }
32666         
32667         this.disabled = state;
32668         
32669         if (state) {
32670             this.el.addClass('disabled');
32671             return;
32672         }
32673         
32674         this.el.removeClass('disabled');
32675     },
32676     
32677     tooltipEl : function()
32678     {
32679         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32680     }
32681 });
32682  
32683
32684  /*
32685  * - LGPL
32686  *
32687  * FieldLabel
32688  * 
32689  */
32690
32691 /**
32692  * @class Roo.bootstrap.FieldLabel
32693  * @extends Roo.bootstrap.Component
32694  * Bootstrap FieldLabel class
32695  * @cfg {String} html contents of the element
32696  * @cfg {String} tag tag of the element default label
32697  * @cfg {String} cls class of the element
32698  * @cfg {String} target label target 
32699  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32700  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32701  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32702  * @cfg {String} iconTooltip default "This field is required"
32703  * @cfg {String} indicatorpos (left|right) default left
32704  * 
32705  * @constructor
32706  * Create a new FieldLabel
32707  * @param {Object} config The config object
32708  */
32709
32710 Roo.bootstrap.FieldLabel = function(config){
32711     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32712     
32713     this.addEvents({
32714             /**
32715              * @event invalid
32716              * Fires after the field has been marked as invalid.
32717              * @param {Roo.form.FieldLabel} this
32718              * @param {String} msg The validation message
32719              */
32720             invalid : true,
32721             /**
32722              * @event valid
32723              * Fires after the field has been validated with no errors.
32724              * @param {Roo.form.FieldLabel} this
32725              */
32726             valid : true
32727         });
32728 };
32729
32730 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32731     
32732     tag: 'label',
32733     cls: '',
32734     html: '',
32735     target: '',
32736     allowBlank : true,
32737     invalidClass : 'has-warning',
32738     validClass : 'has-success',
32739     iconTooltip : 'This field is required',
32740     indicatorpos : 'left',
32741     
32742     getAutoCreate : function(){
32743         
32744         var cls = "";
32745         if (!this.allowBlank) {
32746             cls  = "visible";
32747         }
32748         
32749         var cfg = {
32750             tag : this.tag,
32751             cls : 'roo-bootstrap-field-label ' + this.cls,
32752             for : this.target,
32753             cn : [
32754                 {
32755                     tag : 'i',
32756                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32757                     tooltip : this.iconTooltip
32758                 },
32759                 {
32760                     tag : 'span',
32761                     html : this.html
32762                 }
32763             ] 
32764         };
32765         
32766         if(this.indicatorpos == 'right'){
32767             var cfg = {
32768                 tag : this.tag,
32769                 cls : 'roo-bootstrap-field-label ' + this.cls,
32770                 for : this.target,
32771                 cn : [
32772                     {
32773                         tag : 'span',
32774                         html : this.html
32775                     },
32776                     {
32777                         tag : 'i',
32778                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32779                         tooltip : this.iconTooltip
32780                     }
32781                 ] 
32782             };
32783         }
32784         
32785         return cfg;
32786     },
32787     
32788     initEvents: function() 
32789     {
32790         Roo.bootstrap.Element.superclass.initEvents.call(this);
32791         
32792         this.indicator = this.indicatorEl();
32793         
32794         if(this.indicator){
32795             this.indicator.removeClass('visible');
32796             this.indicator.addClass('invisible');
32797         }
32798         
32799         Roo.bootstrap.FieldLabel.register(this);
32800     },
32801     
32802     indicatorEl : function()
32803     {
32804         var indicator = this.el.select('i.roo-required-indicator',true).first();
32805         
32806         if(!indicator){
32807             return false;
32808         }
32809         
32810         return indicator;
32811         
32812     },
32813     
32814     /**
32815      * Mark this field as valid
32816      */
32817     markValid : function()
32818     {
32819         if(this.indicator){
32820             this.indicator.removeClass('visible');
32821             this.indicator.addClass('invisible');
32822         }
32823         if (Roo.bootstrap.version == 3) {
32824             this.el.removeClass(this.invalidClass);
32825             this.el.addClass(this.validClass);
32826         } else {
32827             this.el.removeClass('is-invalid');
32828             this.el.addClass('is-valid');
32829         }
32830         
32831         
32832         this.fireEvent('valid', this);
32833     },
32834     
32835     /**
32836      * Mark this field as invalid
32837      * @param {String} msg The validation message
32838      */
32839     markInvalid : function(msg)
32840     {
32841         if(this.indicator){
32842             this.indicator.removeClass('invisible');
32843             this.indicator.addClass('visible');
32844         }
32845           if (Roo.bootstrap.version == 3) {
32846             this.el.removeClass(this.validClass);
32847             this.el.addClass(this.invalidClass);
32848         } else {
32849             this.el.removeClass('is-valid');
32850             this.el.addClass('is-invalid');
32851         }
32852         
32853         
32854         this.fireEvent('invalid', this, msg);
32855     }
32856     
32857    
32858 });
32859
32860 Roo.apply(Roo.bootstrap.FieldLabel, {
32861     
32862     groups: {},
32863     
32864      /**
32865     * register a FieldLabel Group
32866     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32867     */
32868     register : function(label)
32869     {
32870         if(this.groups.hasOwnProperty(label.target)){
32871             return;
32872         }
32873      
32874         this.groups[label.target] = label;
32875         
32876     },
32877     /**
32878     * fetch a FieldLabel Group based on the target
32879     * @param {string} target
32880     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32881     */
32882     get: function(target) {
32883         if (typeof(this.groups[target]) == 'undefined') {
32884             return false;
32885         }
32886         
32887         return this.groups[target] ;
32888     }
32889 });
32890
32891  
32892
32893  /*
32894  * - LGPL
32895  *
32896  * page DateSplitField.
32897  * 
32898  */
32899
32900
32901 /**
32902  * @class Roo.bootstrap.DateSplitField
32903  * @extends Roo.bootstrap.Component
32904  * Bootstrap DateSplitField class
32905  * @cfg {string} fieldLabel - the label associated
32906  * @cfg {Number} labelWidth set the width of label (0-12)
32907  * @cfg {String} labelAlign (top|left)
32908  * @cfg {Boolean} dayAllowBlank (true|false) default false
32909  * @cfg {Boolean} monthAllowBlank (true|false) default false
32910  * @cfg {Boolean} yearAllowBlank (true|false) default false
32911  * @cfg {string} dayPlaceholder 
32912  * @cfg {string} monthPlaceholder
32913  * @cfg {string} yearPlaceholder
32914  * @cfg {string} dayFormat default 'd'
32915  * @cfg {string} monthFormat default 'm'
32916  * @cfg {string} yearFormat default 'Y'
32917  * @cfg {Number} labellg set the width of label (1-12)
32918  * @cfg {Number} labelmd set the width of label (1-12)
32919  * @cfg {Number} labelsm set the width of label (1-12)
32920  * @cfg {Number} labelxs set the width of label (1-12)
32921
32922  *     
32923  * @constructor
32924  * Create a new DateSplitField
32925  * @param {Object} config The config object
32926  */
32927
32928 Roo.bootstrap.DateSplitField = function(config){
32929     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32930     
32931     this.addEvents({
32932         // raw events
32933          /**
32934          * @event years
32935          * getting the data of years
32936          * @param {Roo.bootstrap.DateSplitField} this
32937          * @param {Object} years
32938          */
32939         "years" : true,
32940         /**
32941          * @event days
32942          * getting the data of days
32943          * @param {Roo.bootstrap.DateSplitField} this
32944          * @param {Object} days
32945          */
32946         "days" : true,
32947         /**
32948          * @event invalid
32949          * Fires after the field has been marked as invalid.
32950          * @param {Roo.form.Field} this
32951          * @param {String} msg The validation message
32952          */
32953         invalid : true,
32954        /**
32955          * @event valid
32956          * Fires after the field has been validated with no errors.
32957          * @param {Roo.form.Field} this
32958          */
32959         valid : true
32960     });
32961 };
32962
32963 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32964     
32965     fieldLabel : '',
32966     labelAlign : 'top',
32967     labelWidth : 3,
32968     dayAllowBlank : false,
32969     monthAllowBlank : false,
32970     yearAllowBlank : false,
32971     dayPlaceholder : '',
32972     monthPlaceholder : '',
32973     yearPlaceholder : '',
32974     dayFormat : 'd',
32975     monthFormat : 'm',
32976     yearFormat : 'Y',
32977     isFormField : true,
32978     labellg : 0,
32979     labelmd : 0,
32980     labelsm : 0,
32981     labelxs : 0,
32982     
32983     getAutoCreate : function()
32984     {
32985         var cfg = {
32986             tag : 'div',
32987             cls : 'row roo-date-split-field-group',
32988             cn : [
32989                 {
32990                     tag : 'input',
32991                     type : 'hidden',
32992                     cls : 'form-hidden-field roo-date-split-field-group-value',
32993                     name : this.name
32994                 }
32995             ]
32996         };
32997         
32998         var labelCls = 'col-md-12';
32999         var contentCls = 'col-md-4';
33000         
33001         if(this.fieldLabel){
33002             
33003             var label = {
33004                 tag : 'div',
33005                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33006                 cn : [
33007                     {
33008                         tag : 'label',
33009                         html : this.fieldLabel
33010                     }
33011                 ]
33012             };
33013             
33014             if(this.labelAlign == 'left'){
33015             
33016                 if(this.labelWidth > 12){
33017                     label.style = "width: " + this.labelWidth + 'px';
33018                 }
33019
33020                 if(this.labelWidth < 13 && this.labelmd == 0){
33021                     this.labelmd = this.labelWidth;
33022                 }
33023
33024                 if(this.labellg > 0){
33025                     labelCls = ' col-lg-' + this.labellg;
33026                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33027                 }
33028
33029                 if(this.labelmd > 0){
33030                     labelCls = ' col-md-' + this.labelmd;
33031                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33032                 }
33033
33034                 if(this.labelsm > 0){
33035                     labelCls = ' col-sm-' + this.labelsm;
33036                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33037                 }
33038
33039                 if(this.labelxs > 0){
33040                     labelCls = ' col-xs-' + this.labelxs;
33041                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33042                 }
33043             }
33044             
33045             label.cls += ' ' + labelCls;
33046             
33047             cfg.cn.push(label);
33048         }
33049         
33050         Roo.each(['day', 'month', 'year'], function(t){
33051             cfg.cn.push({
33052                 tag : 'div',
33053                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33054             });
33055         }, this);
33056         
33057         return cfg;
33058     },
33059     
33060     inputEl: function ()
33061     {
33062         return this.el.select('.roo-date-split-field-group-value', true).first();
33063     },
33064     
33065     onRender : function(ct, position) 
33066     {
33067         var _this = this;
33068         
33069         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33070         
33071         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33072         
33073         this.dayField = new Roo.bootstrap.ComboBox({
33074             allowBlank : this.dayAllowBlank,
33075             alwaysQuery : true,
33076             displayField : 'value',
33077             editable : false,
33078             fieldLabel : '',
33079             forceSelection : true,
33080             mode : 'local',
33081             placeholder : this.dayPlaceholder,
33082             selectOnFocus : true,
33083             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33084             triggerAction : 'all',
33085             typeAhead : true,
33086             valueField : 'value',
33087             store : new Roo.data.SimpleStore({
33088                 data : (function() {    
33089                     var days = [];
33090                     _this.fireEvent('days', _this, days);
33091                     return days;
33092                 })(),
33093                 fields : [ 'value' ]
33094             }),
33095             listeners : {
33096                 select : function (_self, record, index)
33097                 {
33098                     _this.setValue(_this.getValue());
33099                 }
33100             }
33101         });
33102
33103         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33104         
33105         this.monthField = new Roo.bootstrap.MonthField({
33106             after : '<i class=\"fa fa-calendar\"></i>',
33107             allowBlank : this.monthAllowBlank,
33108             placeholder : this.monthPlaceholder,
33109             readOnly : true,
33110             listeners : {
33111                 render : function (_self)
33112                 {
33113                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33114                         e.preventDefault();
33115                         _self.focus();
33116                     });
33117                 },
33118                 select : function (_self, oldvalue, newvalue)
33119                 {
33120                     _this.setValue(_this.getValue());
33121                 }
33122             }
33123         });
33124         
33125         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33126         
33127         this.yearField = new Roo.bootstrap.ComboBox({
33128             allowBlank : this.yearAllowBlank,
33129             alwaysQuery : true,
33130             displayField : 'value',
33131             editable : false,
33132             fieldLabel : '',
33133             forceSelection : true,
33134             mode : 'local',
33135             placeholder : this.yearPlaceholder,
33136             selectOnFocus : true,
33137             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33138             triggerAction : 'all',
33139             typeAhead : true,
33140             valueField : 'value',
33141             store : new Roo.data.SimpleStore({
33142                 data : (function() {
33143                     var years = [];
33144                     _this.fireEvent('years', _this, years);
33145                     return years;
33146                 })(),
33147                 fields : [ 'value' ]
33148             }),
33149             listeners : {
33150                 select : function (_self, record, index)
33151                 {
33152                     _this.setValue(_this.getValue());
33153                 }
33154             }
33155         });
33156
33157         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33158     },
33159     
33160     setValue : function(v, format)
33161     {
33162         this.inputEl.dom.value = v;
33163         
33164         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33165         
33166         var d = Date.parseDate(v, f);
33167         
33168         if(!d){
33169             this.validate();
33170             return;
33171         }
33172         
33173         this.setDay(d.format(this.dayFormat));
33174         this.setMonth(d.format(this.monthFormat));
33175         this.setYear(d.format(this.yearFormat));
33176         
33177         this.validate();
33178         
33179         return;
33180     },
33181     
33182     setDay : function(v)
33183     {
33184         this.dayField.setValue(v);
33185         this.inputEl.dom.value = this.getValue();
33186         this.validate();
33187         return;
33188     },
33189     
33190     setMonth : function(v)
33191     {
33192         this.monthField.setValue(v, true);
33193         this.inputEl.dom.value = this.getValue();
33194         this.validate();
33195         return;
33196     },
33197     
33198     setYear : function(v)
33199     {
33200         this.yearField.setValue(v);
33201         this.inputEl.dom.value = this.getValue();
33202         this.validate();
33203         return;
33204     },
33205     
33206     getDay : function()
33207     {
33208         return this.dayField.getValue();
33209     },
33210     
33211     getMonth : function()
33212     {
33213         return this.monthField.getValue();
33214     },
33215     
33216     getYear : function()
33217     {
33218         return this.yearField.getValue();
33219     },
33220     
33221     getValue : function()
33222     {
33223         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33224         
33225         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33226         
33227         return date;
33228     },
33229     
33230     reset : function()
33231     {
33232         this.setDay('');
33233         this.setMonth('');
33234         this.setYear('');
33235         this.inputEl.dom.value = '';
33236         this.validate();
33237         return;
33238     },
33239     
33240     validate : function()
33241     {
33242         var d = this.dayField.validate();
33243         var m = this.monthField.validate();
33244         var y = this.yearField.validate();
33245         
33246         var valid = true;
33247         
33248         if(
33249                 (!this.dayAllowBlank && !d) ||
33250                 (!this.monthAllowBlank && !m) ||
33251                 (!this.yearAllowBlank && !y)
33252         ){
33253             valid = false;
33254         }
33255         
33256         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33257             return valid;
33258         }
33259         
33260         if(valid){
33261             this.markValid();
33262             return valid;
33263         }
33264         
33265         this.markInvalid();
33266         
33267         return valid;
33268     },
33269     
33270     markValid : function()
33271     {
33272         
33273         var label = this.el.select('label', true).first();
33274         var icon = this.el.select('i.fa-star', true).first();
33275
33276         if(label && icon){
33277             icon.remove();
33278         }
33279         
33280         this.fireEvent('valid', this);
33281     },
33282     
33283      /**
33284      * Mark this field as invalid
33285      * @param {String} msg The validation message
33286      */
33287     markInvalid : function(msg)
33288     {
33289         
33290         var label = this.el.select('label', true).first();
33291         var icon = this.el.select('i.fa-star', true).first();
33292
33293         if(label && !icon){
33294             this.el.select('.roo-date-split-field-label', true).createChild({
33295                 tag : 'i',
33296                 cls : 'text-danger fa fa-lg fa-star',
33297                 tooltip : 'This field is required',
33298                 style : 'margin-right:5px;'
33299             }, label, true);
33300         }
33301         
33302         this.fireEvent('invalid', this, msg);
33303     },
33304     
33305     clearInvalid : function()
33306     {
33307         var label = this.el.select('label', true).first();
33308         var icon = this.el.select('i.fa-star', true).first();
33309
33310         if(label && icon){
33311             icon.remove();
33312         }
33313         
33314         this.fireEvent('valid', this);
33315     },
33316     
33317     getName: function()
33318     {
33319         return this.name;
33320     }
33321     
33322 });
33323
33324  /**
33325  *
33326  * This is based on 
33327  * http://masonry.desandro.com
33328  *
33329  * The idea is to render all the bricks based on vertical width...
33330  *
33331  * The original code extends 'outlayer' - we might need to use that....
33332  * 
33333  */
33334
33335
33336 /**
33337  * @class Roo.bootstrap.LayoutMasonry
33338  * @extends Roo.bootstrap.Component
33339  * Bootstrap Layout Masonry class
33340  * 
33341  * @constructor
33342  * Create a new Element
33343  * @param {Object} config The config object
33344  */
33345
33346 Roo.bootstrap.LayoutMasonry = function(config){
33347     
33348     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33349     
33350     this.bricks = [];
33351     
33352     Roo.bootstrap.LayoutMasonry.register(this);
33353     
33354     this.addEvents({
33355         // raw events
33356         /**
33357          * @event layout
33358          * Fire after layout the items
33359          * @param {Roo.bootstrap.LayoutMasonry} this
33360          * @param {Roo.EventObject} e
33361          */
33362         "layout" : true
33363     });
33364     
33365 };
33366
33367 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33368     
33369     /**
33370      * @cfg {Boolean} isLayoutInstant = no animation?
33371      */   
33372     isLayoutInstant : false, // needed?
33373    
33374     /**
33375      * @cfg {Number} boxWidth  width of the columns
33376      */   
33377     boxWidth : 450,
33378     
33379       /**
33380      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33381      */   
33382     boxHeight : 0,
33383     
33384     /**
33385      * @cfg {Number} padWidth padding below box..
33386      */   
33387     padWidth : 10, 
33388     
33389     /**
33390      * @cfg {Number} gutter gutter width..
33391      */   
33392     gutter : 10,
33393     
33394      /**
33395      * @cfg {Number} maxCols maximum number of columns
33396      */   
33397     
33398     maxCols: 0,
33399     
33400     /**
33401      * @cfg {Boolean} isAutoInitial defalut true
33402      */   
33403     isAutoInitial : true, 
33404     
33405     containerWidth: 0,
33406     
33407     /**
33408      * @cfg {Boolean} isHorizontal defalut false
33409      */   
33410     isHorizontal : false, 
33411
33412     currentSize : null,
33413     
33414     tag: 'div',
33415     
33416     cls: '',
33417     
33418     bricks: null, //CompositeElement
33419     
33420     cols : 1,
33421     
33422     _isLayoutInited : false,
33423     
33424 //    isAlternative : false, // only use for vertical layout...
33425     
33426     /**
33427      * @cfg {Number} alternativePadWidth padding below box..
33428      */   
33429     alternativePadWidth : 50,
33430     
33431     selectedBrick : [],
33432     
33433     getAutoCreate : function(){
33434         
33435         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33436         
33437         var cfg = {
33438             tag: this.tag,
33439             cls: 'blog-masonary-wrapper ' + this.cls,
33440             cn : {
33441                 cls : 'mas-boxes masonary'
33442             }
33443         };
33444         
33445         return cfg;
33446     },
33447     
33448     getChildContainer: function( )
33449     {
33450         if (this.boxesEl) {
33451             return this.boxesEl;
33452         }
33453         
33454         this.boxesEl = this.el.select('.mas-boxes').first();
33455         
33456         return this.boxesEl;
33457     },
33458     
33459     
33460     initEvents : function()
33461     {
33462         var _this = this;
33463         
33464         if(this.isAutoInitial){
33465             Roo.log('hook children rendered');
33466             this.on('childrenrendered', function() {
33467                 Roo.log('children rendered');
33468                 _this.initial();
33469             } ,this);
33470         }
33471     },
33472     
33473     initial : function()
33474     {
33475         this.selectedBrick = [];
33476         
33477         this.currentSize = this.el.getBox(true);
33478         
33479         Roo.EventManager.onWindowResize(this.resize, this); 
33480
33481         if(!this.isAutoInitial){
33482             this.layout();
33483             return;
33484         }
33485         
33486         this.layout();
33487         
33488         return;
33489         //this.layout.defer(500,this);
33490         
33491     },
33492     
33493     resize : function()
33494     {
33495         var cs = this.el.getBox(true);
33496         
33497         if (
33498                 this.currentSize.width == cs.width && 
33499                 this.currentSize.x == cs.x && 
33500                 this.currentSize.height == cs.height && 
33501                 this.currentSize.y == cs.y 
33502         ) {
33503             Roo.log("no change in with or X or Y");
33504             return;
33505         }
33506         
33507         this.currentSize = cs;
33508         
33509         this.layout();
33510         
33511     },
33512     
33513     layout : function()
33514     {   
33515         this._resetLayout();
33516         
33517         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33518         
33519         this.layoutItems( isInstant );
33520       
33521         this._isLayoutInited = true;
33522         
33523         this.fireEvent('layout', this);
33524         
33525     },
33526     
33527     _resetLayout : function()
33528     {
33529         if(this.isHorizontal){
33530             this.horizontalMeasureColumns();
33531             return;
33532         }
33533         
33534         this.verticalMeasureColumns();
33535         
33536     },
33537     
33538     verticalMeasureColumns : function()
33539     {
33540         this.getContainerWidth();
33541         
33542 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33543 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33544 //            return;
33545 //        }
33546         
33547         var boxWidth = this.boxWidth + this.padWidth;
33548         
33549         if(this.containerWidth < this.boxWidth){
33550             boxWidth = this.containerWidth
33551         }
33552         
33553         var containerWidth = this.containerWidth;
33554         
33555         var cols = Math.floor(containerWidth / boxWidth);
33556         
33557         this.cols = Math.max( cols, 1 );
33558         
33559         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33560         
33561         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33562         
33563         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33564         
33565         this.colWidth = boxWidth + avail - this.padWidth;
33566         
33567         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33568         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33569     },
33570     
33571     horizontalMeasureColumns : function()
33572     {
33573         this.getContainerWidth();
33574         
33575         var boxWidth = this.boxWidth;
33576         
33577         if(this.containerWidth < boxWidth){
33578             boxWidth = this.containerWidth;
33579         }
33580         
33581         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33582         
33583         this.el.setHeight(boxWidth);
33584         
33585     },
33586     
33587     getContainerWidth : function()
33588     {
33589         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33590     },
33591     
33592     layoutItems : function( isInstant )
33593     {
33594         Roo.log(this.bricks);
33595         
33596         var items = Roo.apply([], this.bricks);
33597         
33598         if(this.isHorizontal){
33599             this._horizontalLayoutItems( items , isInstant );
33600             return;
33601         }
33602         
33603 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33604 //            this._verticalAlternativeLayoutItems( items , isInstant );
33605 //            return;
33606 //        }
33607         
33608         this._verticalLayoutItems( items , isInstant );
33609         
33610     },
33611     
33612     _verticalLayoutItems : function ( items , isInstant)
33613     {
33614         if ( !items || !items.length ) {
33615             return;
33616         }
33617         
33618         var standard = [
33619             ['xs', 'xs', 'xs', 'tall'],
33620             ['xs', 'xs', 'tall'],
33621             ['xs', 'xs', 'sm'],
33622             ['xs', 'xs', 'xs'],
33623             ['xs', 'tall'],
33624             ['xs', 'sm'],
33625             ['xs', 'xs'],
33626             ['xs'],
33627             
33628             ['sm', 'xs', 'xs'],
33629             ['sm', 'xs'],
33630             ['sm'],
33631             
33632             ['tall', 'xs', 'xs', 'xs'],
33633             ['tall', 'xs', 'xs'],
33634             ['tall', 'xs'],
33635             ['tall']
33636             
33637         ];
33638         
33639         var queue = [];
33640         
33641         var boxes = [];
33642         
33643         var box = [];
33644         
33645         Roo.each(items, function(item, k){
33646             
33647             switch (item.size) {
33648                 // these layouts take up a full box,
33649                 case 'md' :
33650                 case 'md-left' :
33651                 case 'md-right' :
33652                 case 'wide' :
33653                     
33654                     if(box.length){
33655                         boxes.push(box);
33656                         box = [];
33657                     }
33658                     
33659                     boxes.push([item]);
33660                     
33661                     break;
33662                     
33663                 case 'xs' :
33664                 case 'sm' :
33665                 case 'tall' :
33666                     
33667                     box.push(item);
33668                     
33669                     break;
33670                 default :
33671                     break;
33672                     
33673             }
33674             
33675         }, this);
33676         
33677         if(box.length){
33678             boxes.push(box);
33679             box = [];
33680         }
33681         
33682         var filterPattern = function(box, length)
33683         {
33684             if(!box.length){
33685                 return;
33686             }
33687             
33688             var match = false;
33689             
33690             var pattern = box.slice(0, length);
33691             
33692             var format = [];
33693             
33694             Roo.each(pattern, function(i){
33695                 format.push(i.size);
33696             }, this);
33697             
33698             Roo.each(standard, function(s){
33699                 
33700                 if(String(s) != String(format)){
33701                     return;
33702                 }
33703                 
33704                 match = true;
33705                 return false;
33706                 
33707             }, this);
33708             
33709             if(!match && length == 1){
33710                 return;
33711             }
33712             
33713             if(!match){
33714                 filterPattern(box, length - 1);
33715                 return;
33716             }
33717                 
33718             queue.push(pattern);
33719
33720             box = box.slice(length, box.length);
33721
33722             filterPattern(box, 4);
33723
33724             return;
33725             
33726         }
33727         
33728         Roo.each(boxes, function(box, k){
33729             
33730             if(!box.length){
33731                 return;
33732             }
33733             
33734             if(box.length == 1){
33735                 queue.push(box);
33736                 return;
33737             }
33738             
33739             filterPattern(box, 4);
33740             
33741         }, this);
33742         
33743         this._processVerticalLayoutQueue( queue, isInstant );
33744         
33745     },
33746     
33747 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33748 //    {
33749 //        if ( !items || !items.length ) {
33750 //            return;
33751 //        }
33752 //
33753 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33754 //        
33755 //    },
33756     
33757     _horizontalLayoutItems : function ( items , isInstant)
33758     {
33759         if ( !items || !items.length || items.length < 3) {
33760             return;
33761         }
33762         
33763         items.reverse();
33764         
33765         var eItems = items.slice(0, 3);
33766         
33767         items = items.slice(3, items.length);
33768         
33769         var standard = [
33770             ['xs', 'xs', 'xs', 'wide'],
33771             ['xs', 'xs', 'wide'],
33772             ['xs', 'xs', 'sm'],
33773             ['xs', 'xs', 'xs'],
33774             ['xs', 'wide'],
33775             ['xs', 'sm'],
33776             ['xs', 'xs'],
33777             ['xs'],
33778             
33779             ['sm', 'xs', 'xs'],
33780             ['sm', 'xs'],
33781             ['sm'],
33782             
33783             ['wide', 'xs', 'xs', 'xs'],
33784             ['wide', 'xs', 'xs'],
33785             ['wide', 'xs'],
33786             ['wide'],
33787             
33788             ['wide-thin']
33789         ];
33790         
33791         var queue = [];
33792         
33793         var boxes = [];
33794         
33795         var box = [];
33796         
33797         Roo.each(items, function(item, k){
33798             
33799             switch (item.size) {
33800                 case 'md' :
33801                 case 'md-left' :
33802                 case 'md-right' :
33803                 case 'tall' :
33804                     
33805                     if(box.length){
33806                         boxes.push(box);
33807                         box = [];
33808                     }
33809                     
33810                     boxes.push([item]);
33811                     
33812                     break;
33813                     
33814                 case 'xs' :
33815                 case 'sm' :
33816                 case 'wide' :
33817                 case 'wide-thin' :
33818                     
33819                     box.push(item);
33820                     
33821                     break;
33822                 default :
33823                     break;
33824                     
33825             }
33826             
33827         }, this);
33828         
33829         if(box.length){
33830             boxes.push(box);
33831             box = [];
33832         }
33833         
33834         var filterPattern = function(box, length)
33835         {
33836             if(!box.length){
33837                 return;
33838             }
33839             
33840             var match = false;
33841             
33842             var pattern = box.slice(0, length);
33843             
33844             var format = [];
33845             
33846             Roo.each(pattern, function(i){
33847                 format.push(i.size);
33848             }, this);
33849             
33850             Roo.each(standard, function(s){
33851                 
33852                 if(String(s) != String(format)){
33853                     return;
33854                 }
33855                 
33856                 match = true;
33857                 return false;
33858                 
33859             }, this);
33860             
33861             if(!match && length == 1){
33862                 return;
33863             }
33864             
33865             if(!match){
33866                 filterPattern(box, length - 1);
33867                 return;
33868             }
33869                 
33870             queue.push(pattern);
33871
33872             box = box.slice(length, box.length);
33873
33874             filterPattern(box, 4);
33875
33876             return;
33877             
33878         }
33879         
33880         Roo.each(boxes, function(box, k){
33881             
33882             if(!box.length){
33883                 return;
33884             }
33885             
33886             if(box.length == 1){
33887                 queue.push(box);
33888                 return;
33889             }
33890             
33891             filterPattern(box, 4);
33892             
33893         }, this);
33894         
33895         
33896         var prune = [];
33897         
33898         var pos = this.el.getBox(true);
33899         
33900         var minX = pos.x;
33901         
33902         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33903         
33904         var hit_end = false;
33905         
33906         Roo.each(queue, function(box){
33907             
33908             if(hit_end){
33909                 
33910                 Roo.each(box, function(b){
33911                 
33912                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33913                     b.el.hide();
33914
33915                 }, this);
33916
33917                 return;
33918             }
33919             
33920             var mx = 0;
33921             
33922             Roo.each(box, function(b){
33923                 
33924                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33925                 b.el.show();
33926
33927                 mx = Math.max(mx, b.x);
33928                 
33929             }, this);
33930             
33931             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33932             
33933             if(maxX < minX){
33934                 
33935                 Roo.each(box, function(b){
33936                 
33937                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33938                     b.el.hide();
33939                     
33940                 }, this);
33941                 
33942                 hit_end = true;
33943                 
33944                 return;
33945             }
33946             
33947             prune.push(box);
33948             
33949         }, this);
33950         
33951         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33952     },
33953     
33954     /** Sets position of item in DOM
33955     * @param {Element} item
33956     * @param {Number} x - horizontal position
33957     * @param {Number} y - vertical position
33958     * @param {Boolean} isInstant - disables transitions
33959     */
33960     _processVerticalLayoutQueue : function( queue, isInstant )
33961     {
33962         var pos = this.el.getBox(true);
33963         var x = pos.x;
33964         var y = pos.y;
33965         var maxY = [];
33966         
33967         for (var i = 0; i < this.cols; i++){
33968             maxY[i] = pos.y;
33969         }
33970         
33971         Roo.each(queue, function(box, k){
33972             
33973             var col = k % this.cols;
33974             
33975             Roo.each(box, function(b,kk){
33976                 
33977                 b.el.position('absolute');
33978                 
33979                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33980                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33981                 
33982                 if(b.size == 'md-left' || b.size == 'md-right'){
33983                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33984                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33985                 }
33986                 
33987                 b.el.setWidth(width);
33988                 b.el.setHeight(height);
33989                 // iframe?
33990                 b.el.select('iframe',true).setSize(width,height);
33991                 
33992             }, this);
33993             
33994             for (var i = 0; i < this.cols; i++){
33995                 
33996                 if(maxY[i] < maxY[col]){
33997                     col = i;
33998                     continue;
33999                 }
34000                 
34001                 col = Math.min(col, i);
34002                 
34003             }
34004             
34005             x = pos.x + col * (this.colWidth + this.padWidth);
34006             
34007             y = maxY[col];
34008             
34009             var positions = [];
34010             
34011             switch (box.length){
34012                 case 1 :
34013                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34014                     break;
34015                 case 2 :
34016                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34017                     break;
34018                 case 3 :
34019                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34020                     break;
34021                 case 4 :
34022                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34023                     break;
34024                 default :
34025                     break;
34026             }
34027             
34028             Roo.each(box, function(b,kk){
34029                 
34030                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34031                 
34032                 var sz = b.el.getSize();
34033                 
34034                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34035                 
34036             }, this);
34037             
34038         }, this);
34039         
34040         var mY = 0;
34041         
34042         for (var i = 0; i < this.cols; i++){
34043             mY = Math.max(mY, maxY[i]);
34044         }
34045         
34046         this.el.setHeight(mY - pos.y);
34047         
34048     },
34049     
34050 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34051 //    {
34052 //        var pos = this.el.getBox(true);
34053 //        var x = pos.x;
34054 //        var y = pos.y;
34055 //        var maxX = pos.right;
34056 //        
34057 //        var maxHeight = 0;
34058 //        
34059 //        Roo.each(items, function(item, k){
34060 //            
34061 //            var c = k % 2;
34062 //            
34063 //            item.el.position('absolute');
34064 //                
34065 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34066 //
34067 //            item.el.setWidth(width);
34068 //
34069 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34070 //
34071 //            item.el.setHeight(height);
34072 //            
34073 //            if(c == 0){
34074 //                item.el.setXY([x, y], isInstant ? false : true);
34075 //            } else {
34076 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34077 //            }
34078 //            
34079 //            y = y + height + this.alternativePadWidth;
34080 //            
34081 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34082 //            
34083 //        }, this);
34084 //        
34085 //        this.el.setHeight(maxHeight);
34086 //        
34087 //    },
34088     
34089     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34090     {
34091         var pos = this.el.getBox(true);
34092         
34093         var minX = pos.x;
34094         var minY = pos.y;
34095         
34096         var maxX = pos.right;
34097         
34098         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34099         
34100         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34101         
34102         Roo.each(queue, function(box, k){
34103             
34104             Roo.each(box, function(b, kk){
34105                 
34106                 b.el.position('absolute');
34107                 
34108                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34109                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34110                 
34111                 if(b.size == 'md-left' || b.size == 'md-right'){
34112                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34113                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34114                 }
34115                 
34116                 b.el.setWidth(width);
34117                 b.el.setHeight(height);
34118                 
34119             }, this);
34120             
34121             if(!box.length){
34122                 return;
34123             }
34124             
34125             var positions = [];
34126             
34127             switch (box.length){
34128                 case 1 :
34129                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34130                     break;
34131                 case 2 :
34132                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34133                     break;
34134                 case 3 :
34135                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34136                     break;
34137                 case 4 :
34138                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34139                     break;
34140                 default :
34141                     break;
34142             }
34143             
34144             Roo.each(box, function(b,kk){
34145                 
34146                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34147                 
34148                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34149                 
34150             }, this);
34151             
34152         }, this);
34153         
34154     },
34155     
34156     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34157     {
34158         Roo.each(eItems, function(b,k){
34159             
34160             b.size = (k == 0) ? 'sm' : 'xs';
34161             b.x = (k == 0) ? 2 : 1;
34162             b.y = (k == 0) ? 2 : 1;
34163             
34164             b.el.position('absolute');
34165             
34166             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34167                 
34168             b.el.setWidth(width);
34169             
34170             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34171             
34172             b.el.setHeight(height);
34173             
34174         }, this);
34175
34176         var positions = [];
34177         
34178         positions.push({
34179             x : maxX - this.unitWidth * 2 - this.gutter,
34180             y : minY
34181         });
34182         
34183         positions.push({
34184             x : maxX - this.unitWidth,
34185             y : minY + (this.unitWidth + this.gutter) * 2
34186         });
34187         
34188         positions.push({
34189             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34190             y : minY
34191         });
34192         
34193         Roo.each(eItems, function(b,k){
34194             
34195             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34196
34197         }, this);
34198         
34199     },
34200     
34201     getVerticalOneBoxColPositions : function(x, y, box)
34202     {
34203         var pos = [];
34204         
34205         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34206         
34207         if(box[0].size == 'md-left'){
34208             rand = 0;
34209         }
34210         
34211         if(box[0].size == 'md-right'){
34212             rand = 1;
34213         }
34214         
34215         pos.push({
34216             x : x + (this.unitWidth + this.gutter) * rand,
34217             y : y
34218         });
34219         
34220         return pos;
34221     },
34222     
34223     getVerticalTwoBoxColPositions : function(x, y, box)
34224     {
34225         var pos = [];
34226         
34227         if(box[0].size == 'xs'){
34228             
34229             pos.push({
34230                 x : x,
34231                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34232             });
34233
34234             pos.push({
34235                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34236                 y : y
34237             });
34238             
34239             return pos;
34240             
34241         }
34242         
34243         pos.push({
34244             x : x,
34245             y : y
34246         });
34247
34248         pos.push({
34249             x : x + (this.unitWidth + this.gutter) * 2,
34250             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34251         });
34252         
34253         return pos;
34254         
34255     },
34256     
34257     getVerticalThreeBoxColPositions : function(x, y, box)
34258     {
34259         var pos = [];
34260         
34261         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34262             
34263             pos.push({
34264                 x : x,
34265                 y : y
34266             });
34267
34268             pos.push({
34269                 x : x + (this.unitWidth + this.gutter) * 1,
34270                 y : y
34271             });
34272             
34273             pos.push({
34274                 x : x + (this.unitWidth + this.gutter) * 2,
34275                 y : y
34276             });
34277             
34278             return pos;
34279             
34280         }
34281         
34282         if(box[0].size == 'xs' && box[1].size == 'xs'){
34283             
34284             pos.push({
34285                 x : x,
34286                 y : y
34287             });
34288
34289             pos.push({
34290                 x : x,
34291                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34292             });
34293             
34294             pos.push({
34295                 x : x + (this.unitWidth + this.gutter) * 1,
34296                 y : y
34297             });
34298             
34299             return pos;
34300             
34301         }
34302         
34303         pos.push({
34304             x : x,
34305             y : y
34306         });
34307
34308         pos.push({
34309             x : x + (this.unitWidth + this.gutter) * 2,
34310             y : y
34311         });
34312
34313         pos.push({
34314             x : x + (this.unitWidth + this.gutter) * 2,
34315             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34316         });
34317             
34318         return pos;
34319         
34320     },
34321     
34322     getVerticalFourBoxColPositions : function(x, y, box)
34323     {
34324         var pos = [];
34325         
34326         if(box[0].size == 'xs'){
34327             
34328             pos.push({
34329                 x : x,
34330                 y : y
34331             });
34332
34333             pos.push({
34334                 x : x,
34335                 y : y + (this.unitHeight + this.gutter) * 1
34336             });
34337             
34338             pos.push({
34339                 x : x,
34340                 y : y + (this.unitHeight + this.gutter) * 2
34341             });
34342             
34343             pos.push({
34344                 x : x + (this.unitWidth + this.gutter) * 1,
34345                 y : y
34346             });
34347             
34348             return pos;
34349             
34350         }
34351         
34352         pos.push({
34353             x : x,
34354             y : y
34355         });
34356
34357         pos.push({
34358             x : x + (this.unitWidth + this.gutter) * 2,
34359             y : y
34360         });
34361
34362         pos.push({
34363             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34364             y : y + (this.unitHeight + this.gutter) * 1
34365         });
34366
34367         pos.push({
34368             x : x + (this.unitWidth + this.gutter) * 2,
34369             y : y + (this.unitWidth + this.gutter) * 2
34370         });
34371
34372         return pos;
34373         
34374     },
34375     
34376     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34377     {
34378         var pos = [];
34379         
34380         if(box[0].size == 'md-left'){
34381             pos.push({
34382                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34383                 y : minY
34384             });
34385             
34386             return pos;
34387         }
34388         
34389         if(box[0].size == 'md-right'){
34390             pos.push({
34391                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34392                 y : minY + (this.unitWidth + this.gutter) * 1
34393             });
34394             
34395             return pos;
34396         }
34397         
34398         var rand = Math.floor(Math.random() * (4 - box[0].y));
34399         
34400         pos.push({
34401             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34402             y : minY + (this.unitWidth + this.gutter) * rand
34403         });
34404         
34405         return pos;
34406         
34407     },
34408     
34409     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34410     {
34411         var pos = [];
34412         
34413         if(box[0].size == 'xs'){
34414             
34415             pos.push({
34416                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34417                 y : minY
34418             });
34419
34420             pos.push({
34421                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34422                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34423             });
34424             
34425             return pos;
34426             
34427         }
34428         
34429         pos.push({
34430             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34431             y : minY
34432         });
34433
34434         pos.push({
34435             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34436             y : minY + (this.unitWidth + this.gutter) * 2
34437         });
34438         
34439         return pos;
34440         
34441     },
34442     
34443     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34444     {
34445         var pos = [];
34446         
34447         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34448             
34449             pos.push({
34450                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34451                 y : minY
34452             });
34453
34454             pos.push({
34455                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34456                 y : minY + (this.unitWidth + this.gutter) * 1
34457             });
34458             
34459             pos.push({
34460                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34461                 y : minY + (this.unitWidth + this.gutter) * 2
34462             });
34463             
34464             return pos;
34465             
34466         }
34467         
34468         if(box[0].size == 'xs' && box[1].size == 'xs'){
34469             
34470             pos.push({
34471                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34472                 y : minY
34473             });
34474
34475             pos.push({
34476                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34477                 y : minY
34478             });
34479             
34480             pos.push({
34481                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34482                 y : minY + (this.unitWidth + this.gutter) * 1
34483             });
34484             
34485             return pos;
34486             
34487         }
34488         
34489         pos.push({
34490             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34491             y : minY
34492         });
34493
34494         pos.push({
34495             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34496             y : minY + (this.unitWidth + this.gutter) * 2
34497         });
34498
34499         pos.push({
34500             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34501             y : minY + (this.unitWidth + this.gutter) * 2
34502         });
34503             
34504         return pos;
34505         
34506     },
34507     
34508     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34509     {
34510         var pos = [];
34511         
34512         if(box[0].size == 'xs'){
34513             
34514             pos.push({
34515                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34516                 y : minY
34517             });
34518
34519             pos.push({
34520                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34521                 y : minY
34522             });
34523             
34524             pos.push({
34525                 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),
34526                 y : minY
34527             });
34528             
34529             pos.push({
34530                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34531                 y : minY + (this.unitWidth + this.gutter) * 1
34532             });
34533             
34534             return pos;
34535             
34536         }
34537         
34538         pos.push({
34539             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34540             y : minY
34541         });
34542         
34543         pos.push({
34544             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34545             y : minY + (this.unitWidth + this.gutter) * 2
34546         });
34547         
34548         pos.push({
34549             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34550             y : minY + (this.unitWidth + this.gutter) * 2
34551         });
34552         
34553         pos.push({
34554             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),
34555             y : minY + (this.unitWidth + this.gutter) * 2
34556         });
34557
34558         return pos;
34559         
34560     },
34561     
34562     /**
34563     * remove a Masonry Brick
34564     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34565     */
34566     removeBrick : function(brick_id)
34567     {
34568         if (!brick_id) {
34569             return;
34570         }
34571         
34572         for (var i = 0; i<this.bricks.length; i++) {
34573             if (this.bricks[i].id == brick_id) {
34574                 this.bricks.splice(i,1);
34575                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34576                 this.initial();
34577             }
34578         }
34579     },
34580     
34581     /**
34582     * adds a Masonry Brick
34583     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34584     */
34585     addBrick : function(cfg)
34586     {
34587         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34588         //this.register(cn);
34589         cn.parentId = this.id;
34590         cn.render(this.el);
34591         return cn;
34592     },
34593     
34594     /**
34595     * register a Masonry Brick
34596     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34597     */
34598     
34599     register : function(brick)
34600     {
34601         this.bricks.push(brick);
34602         brick.masonryId = this.id;
34603     },
34604     
34605     /**
34606     * clear all the Masonry Brick
34607     */
34608     clearAll : function()
34609     {
34610         this.bricks = [];
34611         //this.getChildContainer().dom.innerHTML = "";
34612         this.el.dom.innerHTML = '';
34613     },
34614     
34615     getSelected : function()
34616     {
34617         if (!this.selectedBrick) {
34618             return false;
34619         }
34620         
34621         return this.selectedBrick;
34622     }
34623 });
34624
34625 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34626     
34627     groups: {},
34628      /**
34629     * register a Masonry Layout
34630     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34631     */
34632     
34633     register : function(layout)
34634     {
34635         this.groups[layout.id] = layout;
34636     },
34637     /**
34638     * fetch a  Masonry Layout based on the masonry layout ID
34639     * @param {string} the masonry layout to add
34640     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34641     */
34642     
34643     get: function(layout_id) {
34644         if (typeof(this.groups[layout_id]) == 'undefined') {
34645             return false;
34646         }
34647         return this.groups[layout_id] ;
34648     }
34649     
34650     
34651     
34652 });
34653
34654  
34655
34656  /**
34657  *
34658  * This is based on 
34659  * http://masonry.desandro.com
34660  *
34661  * The idea is to render all the bricks based on vertical width...
34662  *
34663  * The original code extends 'outlayer' - we might need to use that....
34664  * 
34665  */
34666
34667
34668 /**
34669  * @class Roo.bootstrap.LayoutMasonryAuto
34670  * @extends Roo.bootstrap.Component
34671  * Bootstrap Layout Masonry class
34672  * 
34673  * @constructor
34674  * Create a new Element
34675  * @param {Object} config The config object
34676  */
34677
34678 Roo.bootstrap.LayoutMasonryAuto = function(config){
34679     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34680 };
34681
34682 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34683     
34684       /**
34685      * @cfg {Boolean} isFitWidth  - resize the width..
34686      */   
34687     isFitWidth : false,  // options..
34688     /**
34689      * @cfg {Boolean} isOriginLeft = left align?
34690      */   
34691     isOriginLeft : true,
34692     /**
34693      * @cfg {Boolean} isOriginTop = top align?
34694      */   
34695     isOriginTop : false,
34696     /**
34697      * @cfg {Boolean} isLayoutInstant = no animation?
34698      */   
34699     isLayoutInstant : false, // needed?
34700     /**
34701      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34702      */   
34703     isResizingContainer : true,
34704     /**
34705      * @cfg {Number} columnWidth  width of the columns 
34706      */   
34707     
34708     columnWidth : 0,
34709     
34710     /**
34711      * @cfg {Number} maxCols maximum number of columns
34712      */   
34713     
34714     maxCols: 0,
34715     /**
34716      * @cfg {Number} padHeight padding below box..
34717      */   
34718     
34719     padHeight : 10, 
34720     
34721     /**
34722      * @cfg {Boolean} isAutoInitial defalut true
34723      */   
34724     
34725     isAutoInitial : true, 
34726     
34727     // private?
34728     gutter : 0,
34729     
34730     containerWidth: 0,
34731     initialColumnWidth : 0,
34732     currentSize : null,
34733     
34734     colYs : null, // array.
34735     maxY : 0,
34736     padWidth: 10,
34737     
34738     
34739     tag: 'div',
34740     cls: '',
34741     bricks: null, //CompositeElement
34742     cols : 0, // array?
34743     // element : null, // wrapped now this.el
34744     _isLayoutInited : null, 
34745     
34746     
34747     getAutoCreate : function(){
34748         
34749         var cfg = {
34750             tag: this.tag,
34751             cls: 'blog-masonary-wrapper ' + this.cls,
34752             cn : {
34753                 cls : 'mas-boxes masonary'
34754             }
34755         };
34756         
34757         return cfg;
34758     },
34759     
34760     getChildContainer: function( )
34761     {
34762         if (this.boxesEl) {
34763             return this.boxesEl;
34764         }
34765         
34766         this.boxesEl = this.el.select('.mas-boxes').first();
34767         
34768         return this.boxesEl;
34769     },
34770     
34771     
34772     initEvents : function()
34773     {
34774         var _this = this;
34775         
34776         if(this.isAutoInitial){
34777             Roo.log('hook children rendered');
34778             this.on('childrenrendered', function() {
34779                 Roo.log('children rendered');
34780                 _this.initial();
34781             } ,this);
34782         }
34783         
34784     },
34785     
34786     initial : function()
34787     {
34788         this.reloadItems();
34789
34790         this.currentSize = this.el.getBox(true);
34791
34792         /// was window resize... - let's see if this works..
34793         Roo.EventManager.onWindowResize(this.resize, this); 
34794
34795         if(!this.isAutoInitial){
34796             this.layout();
34797             return;
34798         }
34799         
34800         this.layout.defer(500,this);
34801     },
34802     
34803     reloadItems: function()
34804     {
34805         this.bricks = this.el.select('.masonry-brick', true);
34806         
34807         this.bricks.each(function(b) {
34808             //Roo.log(b.getSize());
34809             if (!b.attr('originalwidth')) {
34810                 b.attr('originalwidth',  b.getSize().width);
34811             }
34812             
34813         });
34814         
34815         Roo.log(this.bricks.elements.length);
34816     },
34817     
34818     resize : function()
34819     {
34820         Roo.log('resize');
34821         var cs = this.el.getBox(true);
34822         
34823         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34824             Roo.log("no change in with or X");
34825             return;
34826         }
34827         this.currentSize = cs;
34828         this.layout();
34829     },
34830     
34831     layout : function()
34832     {
34833          Roo.log('layout');
34834         this._resetLayout();
34835         //this._manageStamps();
34836       
34837         // don't animate first layout
34838         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34839         this.layoutItems( isInstant );
34840       
34841         // flag for initalized
34842         this._isLayoutInited = true;
34843     },
34844     
34845     layoutItems : function( isInstant )
34846     {
34847         //var items = this._getItemsForLayout( this.items );
34848         // original code supports filtering layout items.. we just ignore it..
34849         
34850         this._layoutItems( this.bricks , isInstant );
34851       
34852         this._postLayout();
34853     },
34854     _layoutItems : function ( items , isInstant)
34855     {
34856        //this.fireEvent( 'layout', this, items );
34857     
34858
34859         if ( !items || !items.elements.length ) {
34860           // no items, emit event with empty array
34861             return;
34862         }
34863
34864         var queue = [];
34865         items.each(function(item) {
34866             Roo.log("layout item");
34867             Roo.log(item);
34868             // get x/y object from method
34869             var position = this._getItemLayoutPosition( item );
34870             // enqueue
34871             position.item = item;
34872             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34873             queue.push( position );
34874         }, this);
34875       
34876         this._processLayoutQueue( queue );
34877     },
34878     /** Sets position of item in DOM
34879     * @param {Element} item
34880     * @param {Number} x - horizontal position
34881     * @param {Number} y - vertical position
34882     * @param {Boolean} isInstant - disables transitions
34883     */
34884     _processLayoutQueue : function( queue )
34885     {
34886         for ( var i=0, len = queue.length; i < len; i++ ) {
34887             var obj = queue[i];
34888             obj.item.position('absolute');
34889             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34890         }
34891     },
34892       
34893     
34894     /**
34895     * Any logic you want to do after each layout,
34896     * i.e. size the container
34897     */
34898     _postLayout : function()
34899     {
34900         this.resizeContainer();
34901     },
34902     
34903     resizeContainer : function()
34904     {
34905         if ( !this.isResizingContainer ) {
34906             return;
34907         }
34908         var size = this._getContainerSize();
34909         if ( size ) {
34910             this.el.setSize(size.width,size.height);
34911             this.boxesEl.setSize(size.width,size.height);
34912         }
34913     },
34914     
34915     
34916     
34917     _resetLayout : function()
34918     {
34919         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34920         this.colWidth = this.el.getWidth();
34921         //this.gutter = this.el.getWidth(); 
34922         
34923         this.measureColumns();
34924
34925         // reset column Y
34926         var i = this.cols;
34927         this.colYs = [];
34928         while (i--) {
34929             this.colYs.push( 0 );
34930         }
34931     
34932         this.maxY = 0;
34933     },
34934
34935     measureColumns : function()
34936     {
34937         this.getContainerWidth();
34938       // if columnWidth is 0, default to outerWidth of first item
34939         if ( !this.columnWidth ) {
34940             var firstItem = this.bricks.first();
34941             Roo.log(firstItem);
34942             this.columnWidth  = this.containerWidth;
34943             if (firstItem && firstItem.attr('originalwidth') ) {
34944                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34945             }
34946             // columnWidth fall back to item of first element
34947             Roo.log("set column width?");
34948                         this.initialColumnWidth = this.columnWidth  ;
34949
34950             // if first elem has no width, default to size of container
34951             
34952         }
34953         
34954         
34955         if (this.initialColumnWidth) {
34956             this.columnWidth = this.initialColumnWidth;
34957         }
34958         
34959         
34960             
34961         // column width is fixed at the top - however if container width get's smaller we should
34962         // reduce it...
34963         
34964         // this bit calcs how man columns..
34965             
34966         var columnWidth = this.columnWidth += this.gutter;
34967       
34968         // calculate columns
34969         var containerWidth = this.containerWidth + this.gutter;
34970         
34971         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34972         // fix rounding errors, typically with gutters
34973         var excess = columnWidth - containerWidth % columnWidth;
34974         
34975         
34976         // if overshoot is less than a pixel, round up, otherwise floor it
34977         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34978         cols = Math[ mathMethod ]( cols );
34979         this.cols = Math.max( cols, 1 );
34980         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34981         
34982          // padding positioning..
34983         var totalColWidth = this.cols * this.columnWidth;
34984         var padavail = this.containerWidth - totalColWidth;
34985         // so for 2 columns - we need 3 'pads'
34986         
34987         var padNeeded = (1+this.cols) * this.padWidth;
34988         
34989         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34990         
34991         this.columnWidth += padExtra
34992         //this.padWidth = Math.floor(padavail /  ( this.cols));
34993         
34994         // adjust colum width so that padding is fixed??
34995         
34996         // we have 3 columns ... total = width * 3
34997         // we have X left over... that should be used by 
34998         
34999         //if (this.expandC) {
35000             
35001         //}
35002         
35003         
35004         
35005     },
35006     
35007     getContainerWidth : function()
35008     {
35009        /* // container is parent if fit width
35010         var container = this.isFitWidth ? this.element.parentNode : this.element;
35011         // check that this.size and size are there
35012         // IE8 triggers resize on body size change, so they might not be
35013         
35014         var size = getSize( container );  //FIXME
35015         this.containerWidth = size && size.innerWidth; //FIXME
35016         */
35017          
35018         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35019         
35020     },
35021     
35022     _getItemLayoutPosition : function( item )  // what is item?
35023     {
35024         // we resize the item to our columnWidth..
35025       
35026         item.setWidth(this.columnWidth);
35027         item.autoBoxAdjust  = false;
35028         
35029         var sz = item.getSize();
35030  
35031         // how many columns does this brick span
35032         var remainder = this.containerWidth % this.columnWidth;
35033         
35034         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35035         // round if off by 1 pixel, otherwise use ceil
35036         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35037         colSpan = Math.min( colSpan, this.cols );
35038         
35039         // normally this should be '1' as we dont' currently allow multi width columns..
35040         
35041         var colGroup = this._getColGroup( colSpan );
35042         // get the minimum Y value from the columns
35043         var minimumY = Math.min.apply( Math, colGroup );
35044         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35045         
35046         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35047          
35048         // position the brick
35049         var position = {
35050             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35051             y: this.currentSize.y + minimumY + this.padHeight
35052         };
35053         
35054         Roo.log(position);
35055         // apply setHeight to necessary columns
35056         var setHeight = minimumY + sz.height + this.padHeight;
35057         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35058         
35059         var setSpan = this.cols + 1 - colGroup.length;
35060         for ( var i = 0; i < setSpan; i++ ) {
35061           this.colYs[ shortColIndex + i ] = setHeight ;
35062         }
35063       
35064         return position;
35065     },
35066     
35067     /**
35068      * @param {Number} colSpan - number of columns the element spans
35069      * @returns {Array} colGroup
35070      */
35071     _getColGroup : function( colSpan )
35072     {
35073         if ( colSpan < 2 ) {
35074           // if brick spans only one column, use all the column Ys
35075           return this.colYs;
35076         }
35077       
35078         var colGroup = [];
35079         // how many different places could this brick fit horizontally
35080         var groupCount = this.cols + 1 - colSpan;
35081         // for each group potential horizontal position
35082         for ( var i = 0; i < groupCount; i++ ) {
35083           // make an array of colY values for that one group
35084           var groupColYs = this.colYs.slice( i, i + colSpan );
35085           // and get the max value of the array
35086           colGroup[i] = Math.max.apply( Math, groupColYs );
35087         }
35088         return colGroup;
35089     },
35090     /*
35091     _manageStamp : function( stamp )
35092     {
35093         var stampSize =  stamp.getSize();
35094         var offset = stamp.getBox();
35095         // get the columns that this stamp affects
35096         var firstX = this.isOriginLeft ? offset.x : offset.right;
35097         var lastX = firstX + stampSize.width;
35098         var firstCol = Math.floor( firstX / this.columnWidth );
35099         firstCol = Math.max( 0, firstCol );
35100         
35101         var lastCol = Math.floor( lastX / this.columnWidth );
35102         // lastCol should not go over if multiple of columnWidth #425
35103         lastCol -= lastX % this.columnWidth ? 0 : 1;
35104         lastCol = Math.min( this.cols - 1, lastCol );
35105         
35106         // set colYs to bottom of the stamp
35107         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35108             stampSize.height;
35109             
35110         for ( var i = firstCol; i <= lastCol; i++ ) {
35111           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35112         }
35113     },
35114     */
35115     
35116     _getContainerSize : function()
35117     {
35118         this.maxY = Math.max.apply( Math, this.colYs );
35119         var size = {
35120             height: this.maxY
35121         };
35122       
35123         if ( this.isFitWidth ) {
35124             size.width = this._getContainerFitWidth();
35125         }
35126       
35127         return size;
35128     },
35129     
35130     _getContainerFitWidth : function()
35131     {
35132         var unusedCols = 0;
35133         // count unused columns
35134         var i = this.cols;
35135         while ( --i ) {
35136           if ( this.colYs[i] !== 0 ) {
35137             break;
35138           }
35139           unusedCols++;
35140         }
35141         // fit container to columns that have been used
35142         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35143     },
35144     
35145     needsResizeLayout : function()
35146     {
35147         var previousWidth = this.containerWidth;
35148         this.getContainerWidth();
35149         return previousWidth !== this.containerWidth;
35150     }
35151  
35152 });
35153
35154  
35155
35156  /*
35157  * - LGPL
35158  *
35159  * element
35160  * 
35161  */
35162
35163 /**
35164  * @class Roo.bootstrap.MasonryBrick
35165  * @extends Roo.bootstrap.Component
35166  * Bootstrap MasonryBrick class
35167  * 
35168  * @constructor
35169  * Create a new MasonryBrick
35170  * @param {Object} config The config object
35171  */
35172
35173 Roo.bootstrap.MasonryBrick = function(config){
35174     
35175     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35176     
35177     Roo.bootstrap.MasonryBrick.register(this);
35178     
35179     this.addEvents({
35180         // raw events
35181         /**
35182          * @event click
35183          * When a MasonryBrick is clcik
35184          * @param {Roo.bootstrap.MasonryBrick} this
35185          * @param {Roo.EventObject} e
35186          */
35187         "click" : true
35188     });
35189 };
35190
35191 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35192     
35193     /**
35194      * @cfg {String} title
35195      */   
35196     title : '',
35197     /**
35198      * @cfg {String} html
35199      */   
35200     html : '',
35201     /**
35202      * @cfg {String} bgimage
35203      */   
35204     bgimage : '',
35205     /**
35206      * @cfg {String} videourl
35207      */   
35208     videourl : '',
35209     /**
35210      * @cfg {String} cls
35211      */   
35212     cls : '',
35213     /**
35214      * @cfg {String} href
35215      */   
35216     href : '',
35217     /**
35218      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35219      */   
35220     size : 'xs',
35221     
35222     /**
35223      * @cfg {String} placetitle (center|bottom)
35224      */   
35225     placetitle : '',
35226     
35227     /**
35228      * @cfg {Boolean} isFitContainer defalut true
35229      */   
35230     isFitContainer : true, 
35231     
35232     /**
35233      * @cfg {Boolean} preventDefault defalut false
35234      */   
35235     preventDefault : false, 
35236     
35237     /**
35238      * @cfg {Boolean} inverse defalut false
35239      */   
35240     maskInverse : false, 
35241     
35242     getAutoCreate : function()
35243     {
35244         if(!this.isFitContainer){
35245             return this.getSplitAutoCreate();
35246         }
35247         
35248         var cls = 'masonry-brick masonry-brick-full';
35249         
35250         if(this.href.length){
35251             cls += ' masonry-brick-link';
35252         }
35253         
35254         if(this.bgimage.length){
35255             cls += ' masonry-brick-image';
35256         }
35257         
35258         if(this.maskInverse){
35259             cls += ' mask-inverse';
35260         }
35261         
35262         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35263             cls += ' enable-mask';
35264         }
35265         
35266         if(this.size){
35267             cls += ' masonry-' + this.size + '-brick';
35268         }
35269         
35270         if(this.placetitle.length){
35271             
35272             switch (this.placetitle) {
35273                 case 'center' :
35274                     cls += ' masonry-center-title';
35275                     break;
35276                 case 'bottom' :
35277                     cls += ' masonry-bottom-title';
35278                     break;
35279                 default:
35280                     break;
35281             }
35282             
35283         } else {
35284             if(!this.html.length && !this.bgimage.length){
35285                 cls += ' masonry-center-title';
35286             }
35287
35288             if(!this.html.length && this.bgimage.length){
35289                 cls += ' masonry-bottom-title';
35290             }
35291         }
35292         
35293         if(this.cls){
35294             cls += ' ' + this.cls;
35295         }
35296         
35297         var cfg = {
35298             tag: (this.href.length) ? 'a' : 'div',
35299             cls: cls,
35300             cn: [
35301                 {
35302                     tag: 'div',
35303                     cls: 'masonry-brick-mask'
35304                 },
35305                 {
35306                     tag: 'div',
35307                     cls: 'masonry-brick-paragraph',
35308                     cn: []
35309                 }
35310             ]
35311         };
35312         
35313         if(this.href.length){
35314             cfg.href = this.href;
35315         }
35316         
35317         var cn = cfg.cn[1].cn;
35318         
35319         if(this.title.length){
35320             cn.push({
35321                 tag: 'h4',
35322                 cls: 'masonry-brick-title',
35323                 html: this.title
35324             });
35325         }
35326         
35327         if(this.html.length){
35328             cn.push({
35329                 tag: 'p',
35330                 cls: 'masonry-brick-text',
35331                 html: this.html
35332             });
35333         }
35334         
35335         if (!this.title.length && !this.html.length) {
35336             cfg.cn[1].cls += ' hide';
35337         }
35338         
35339         if(this.bgimage.length){
35340             cfg.cn.push({
35341                 tag: 'img',
35342                 cls: 'masonry-brick-image-view',
35343                 src: this.bgimage
35344             });
35345         }
35346         
35347         if(this.videourl.length){
35348             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35349             // youtube support only?
35350             cfg.cn.push({
35351                 tag: 'iframe',
35352                 cls: 'masonry-brick-image-view',
35353                 src: vurl,
35354                 frameborder : 0,
35355                 allowfullscreen : true
35356             });
35357         }
35358         
35359         return cfg;
35360         
35361     },
35362     
35363     getSplitAutoCreate : function()
35364     {
35365         var cls = 'masonry-brick masonry-brick-split';
35366         
35367         if(this.href.length){
35368             cls += ' masonry-brick-link';
35369         }
35370         
35371         if(this.bgimage.length){
35372             cls += ' masonry-brick-image';
35373         }
35374         
35375         if(this.size){
35376             cls += ' masonry-' + this.size + '-brick';
35377         }
35378         
35379         switch (this.placetitle) {
35380             case 'center' :
35381                 cls += ' masonry-center-title';
35382                 break;
35383             case 'bottom' :
35384                 cls += ' masonry-bottom-title';
35385                 break;
35386             default:
35387                 if(!this.bgimage.length){
35388                     cls += ' masonry-center-title';
35389                 }
35390
35391                 if(this.bgimage.length){
35392                     cls += ' masonry-bottom-title';
35393                 }
35394                 break;
35395         }
35396         
35397         if(this.cls){
35398             cls += ' ' + this.cls;
35399         }
35400         
35401         var cfg = {
35402             tag: (this.href.length) ? 'a' : 'div',
35403             cls: cls,
35404             cn: [
35405                 {
35406                     tag: 'div',
35407                     cls: 'masonry-brick-split-head',
35408                     cn: [
35409                         {
35410                             tag: 'div',
35411                             cls: 'masonry-brick-paragraph',
35412                             cn: []
35413                         }
35414                     ]
35415                 },
35416                 {
35417                     tag: 'div',
35418                     cls: 'masonry-brick-split-body',
35419                     cn: []
35420                 }
35421             ]
35422         };
35423         
35424         if(this.href.length){
35425             cfg.href = this.href;
35426         }
35427         
35428         if(this.title.length){
35429             cfg.cn[0].cn[0].cn.push({
35430                 tag: 'h4',
35431                 cls: 'masonry-brick-title',
35432                 html: this.title
35433             });
35434         }
35435         
35436         if(this.html.length){
35437             cfg.cn[1].cn.push({
35438                 tag: 'p',
35439                 cls: 'masonry-brick-text',
35440                 html: this.html
35441             });
35442         }
35443
35444         if(this.bgimage.length){
35445             cfg.cn[0].cn.push({
35446                 tag: 'img',
35447                 cls: 'masonry-brick-image-view',
35448                 src: this.bgimage
35449             });
35450         }
35451         
35452         if(this.videourl.length){
35453             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35454             // youtube support only?
35455             cfg.cn[0].cn.cn.push({
35456                 tag: 'iframe',
35457                 cls: 'masonry-brick-image-view',
35458                 src: vurl,
35459                 frameborder : 0,
35460                 allowfullscreen : true
35461             });
35462         }
35463         
35464         return cfg;
35465     },
35466     
35467     initEvents: function() 
35468     {
35469         switch (this.size) {
35470             case 'xs' :
35471                 this.x = 1;
35472                 this.y = 1;
35473                 break;
35474             case 'sm' :
35475                 this.x = 2;
35476                 this.y = 2;
35477                 break;
35478             case 'md' :
35479             case 'md-left' :
35480             case 'md-right' :
35481                 this.x = 3;
35482                 this.y = 3;
35483                 break;
35484             case 'tall' :
35485                 this.x = 2;
35486                 this.y = 3;
35487                 break;
35488             case 'wide' :
35489                 this.x = 3;
35490                 this.y = 2;
35491                 break;
35492             case 'wide-thin' :
35493                 this.x = 3;
35494                 this.y = 1;
35495                 break;
35496                         
35497             default :
35498                 break;
35499         }
35500         
35501         if(Roo.isTouch){
35502             this.el.on('touchstart', this.onTouchStart, this);
35503             this.el.on('touchmove', this.onTouchMove, this);
35504             this.el.on('touchend', this.onTouchEnd, this);
35505             this.el.on('contextmenu', this.onContextMenu, this);
35506         } else {
35507             this.el.on('mouseenter'  ,this.enter, this);
35508             this.el.on('mouseleave', this.leave, this);
35509             this.el.on('click', this.onClick, this);
35510         }
35511         
35512         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35513             this.parent().bricks.push(this);   
35514         }
35515         
35516     },
35517     
35518     onClick: function(e, el)
35519     {
35520         var time = this.endTimer - this.startTimer;
35521         // Roo.log(e.preventDefault());
35522         if(Roo.isTouch){
35523             if(time > 1000){
35524                 e.preventDefault();
35525                 return;
35526             }
35527         }
35528         
35529         if(!this.preventDefault){
35530             return;
35531         }
35532         
35533         e.preventDefault();
35534         
35535         if (this.activeClass != '') {
35536             this.selectBrick();
35537         }
35538         
35539         this.fireEvent('click', this, e);
35540     },
35541     
35542     enter: function(e, el)
35543     {
35544         e.preventDefault();
35545         
35546         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35547             return;
35548         }
35549         
35550         if(this.bgimage.length && this.html.length){
35551             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35552         }
35553     },
35554     
35555     leave: function(e, el)
35556     {
35557         e.preventDefault();
35558         
35559         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35560             return;
35561         }
35562         
35563         if(this.bgimage.length && this.html.length){
35564             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35565         }
35566     },
35567     
35568     onTouchStart: function(e, el)
35569     {
35570 //        e.preventDefault();
35571         
35572         this.touchmoved = false;
35573         
35574         if(!this.isFitContainer){
35575             return;
35576         }
35577         
35578         if(!this.bgimage.length || !this.html.length){
35579             return;
35580         }
35581         
35582         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35583         
35584         this.timer = new Date().getTime();
35585         
35586     },
35587     
35588     onTouchMove: function(e, el)
35589     {
35590         this.touchmoved = true;
35591     },
35592     
35593     onContextMenu : function(e,el)
35594     {
35595         e.preventDefault();
35596         e.stopPropagation();
35597         return false;
35598     },
35599     
35600     onTouchEnd: function(e, el)
35601     {
35602 //        e.preventDefault();
35603         
35604         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35605         
35606             this.leave(e,el);
35607             
35608             return;
35609         }
35610         
35611         if(!this.bgimage.length || !this.html.length){
35612             
35613             if(this.href.length){
35614                 window.location.href = this.href;
35615             }
35616             
35617             return;
35618         }
35619         
35620         if(!this.isFitContainer){
35621             return;
35622         }
35623         
35624         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35625         
35626         window.location.href = this.href;
35627     },
35628     
35629     //selection on single brick only
35630     selectBrick : function() {
35631         
35632         if (!this.parentId) {
35633             return;
35634         }
35635         
35636         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35637         var index = m.selectedBrick.indexOf(this.id);
35638         
35639         if ( index > -1) {
35640             m.selectedBrick.splice(index,1);
35641             this.el.removeClass(this.activeClass);
35642             return;
35643         }
35644         
35645         for(var i = 0; i < m.selectedBrick.length; i++) {
35646             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35647             b.el.removeClass(b.activeClass);
35648         }
35649         
35650         m.selectedBrick = [];
35651         
35652         m.selectedBrick.push(this.id);
35653         this.el.addClass(this.activeClass);
35654         return;
35655     },
35656     
35657     isSelected : function(){
35658         return this.el.hasClass(this.activeClass);
35659         
35660     }
35661 });
35662
35663 Roo.apply(Roo.bootstrap.MasonryBrick, {
35664     
35665     //groups: {},
35666     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35667      /**
35668     * register a Masonry Brick
35669     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35670     */
35671     
35672     register : function(brick)
35673     {
35674         //this.groups[brick.id] = brick;
35675         this.groups.add(brick.id, brick);
35676     },
35677     /**
35678     * fetch a  masonry brick based on the masonry brick ID
35679     * @param {string} the masonry brick to add
35680     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35681     */
35682     
35683     get: function(brick_id) 
35684     {
35685         // if (typeof(this.groups[brick_id]) == 'undefined') {
35686         //     return false;
35687         // }
35688         // return this.groups[brick_id] ;
35689         
35690         if(this.groups.key(brick_id)) {
35691             return this.groups.key(brick_id);
35692         }
35693         
35694         return false;
35695     }
35696     
35697     
35698     
35699 });
35700
35701  /*
35702  * - LGPL
35703  *
35704  * element
35705  * 
35706  */
35707
35708 /**
35709  * @class Roo.bootstrap.Brick
35710  * @extends Roo.bootstrap.Component
35711  * Bootstrap Brick class
35712  * 
35713  * @constructor
35714  * Create a new Brick
35715  * @param {Object} config The config object
35716  */
35717
35718 Roo.bootstrap.Brick = function(config){
35719     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35720     
35721     this.addEvents({
35722         // raw events
35723         /**
35724          * @event click
35725          * When a Brick is click
35726          * @param {Roo.bootstrap.Brick} this
35727          * @param {Roo.EventObject} e
35728          */
35729         "click" : true
35730     });
35731 };
35732
35733 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35734     
35735     /**
35736      * @cfg {String} title
35737      */   
35738     title : '',
35739     /**
35740      * @cfg {String} html
35741      */   
35742     html : '',
35743     /**
35744      * @cfg {String} bgimage
35745      */   
35746     bgimage : '',
35747     /**
35748      * @cfg {String} cls
35749      */   
35750     cls : '',
35751     /**
35752      * @cfg {String} href
35753      */   
35754     href : '',
35755     /**
35756      * @cfg {String} video
35757      */   
35758     video : '',
35759     /**
35760      * @cfg {Boolean} square
35761      */   
35762     square : true,
35763     
35764     getAutoCreate : function()
35765     {
35766         var cls = 'roo-brick';
35767         
35768         if(this.href.length){
35769             cls += ' roo-brick-link';
35770         }
35771         
35772         if(this.bgimage.length){
35773             cls += ' roo-brick-image';
35774         }
35775         
35776         if(!this.html.length && !this.bgimage.length){
35777             cls += ' roo-brick-center-title';
35778         }
35779         
35780         if(!this.html.length && this.bgimage.length){
35781             cls += ' roo-brick-bottom-title';
35782         }
35783         
35784         if(this.cls){
35785             cls += ' ' + this.cls;
35786         }
35787         
35788         var cfg = {
35789             tag: (this.href.length) ? 'a' : 'div',
35790             cls: cls,
35791             cn: [
35792                 {
35793                     tag: 'div',
35794                     cls: 'roo-brick-paragraph',
35795                     cn: []
35796                 }
35797             ]
35798         };
35799         
35800         if(this.href.length){
35801             cfg.href = this.href;
35802         }
35803         
35804         var cn = cfg.cn[0].cn;
35805         
35806         if(this.title.length){
35807             cn.push({
35808                 tag: 'h4',
35809                 cls: 'roo-brick-title',
35810                 html: this.title
35811             });
35812         }
35813         
35814         if(this.html.length){
35815             cn.push({
35816                 tag: 'p',
35817                 cls: 'roo-brick-text',
35818                 html: this.html
35819             });
35820         } else {
35821             cn.cls += ' hide';
35822         }
35823         
35824         if(this.bgimage.length){
35825             cfg.cn.push({
35826                 tag: 'img',
35827                 cls: 'roo-brick-image-view',
35828                 src: this.bgimage
35829             });
35830         }
35831         
35832         return cfg;
35833     },
35834     
35835     initEvents: function() 
35836     {
35837         if(this.title.length || this.html.length){
35838             this.el.on('mouseenter'  ,this.enter, this);
35839             this.el.on('mouseleave', this.leave, this);
35840         }
35841         
35842         Roo.EventManager.onWindowResize(this.resize, this); 
35843         
35844         if(this.bgimage.length){
35845             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35846             this.imageEl.on('load', this.onImageLoad, this);
35847             return;
35848         }
35849         
35850         this.resize();
35851     },
35852     
35853     onImageLoad : function()
35854     {
35855         this.resize();
35856     },
35857     
35858     resize : function()
35859     {
35860         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35861         
35862         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35863         
35864         if(this.bgimage.length){
35865             var image = this.el.select('.roo-brick-image-view', true).first();
35866             
35867             image.setWidth(paragraph.getWidth());
35868             
35869             if(this.square){
35870                 image.setHeight(paragraph.getWidth());
35871             }
35872             
35873             this.el.setHeight(image.getHeight());
35874             paragraph.setHeight(image.getHeight());
35875             
35876         }
35877         
35878     },
35879     
35880     enter: function(e, el)
35881     {
35882         e.preventDefault();
35883         
35884         if(this.bgimage.length){
35885             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35886             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35887         }
35888     },
35889     
35890     leave: function(e, el)
35891     {
35892         e.preventDefault();
35893         
35894         if(this.bgimage.length){
35895             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35896             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35897         }
35898     }
35899     
35900 });
35901
35902  
35903
35904  /*
35905  * - LGPL
35906  *
35907  * Number field 
35908  */
35909
35910 /**
35911  * @class Roo.bootstrap.NumberField
35912  * @extends Roo.bootstrap.Input
35913  * Bootstrap NumberField class
35914  * 
35915  * 
35916  * 
35917  * 
35918  * @constructor
35919  * Create a new NumberField
35920  * @param {Object} config The config object
35921  */
35922
35923 Roo.bootstrap.NumberField = function(config){
35924     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35925 };
35926
35927 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35928     
35929     /**
35930      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35931      */
35932     allowDecimals : true,
35933     /**
35934      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35935      */
35936     decimalSeparator : ".",
35937     /**
35938      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35939      */
35940     decimalPrecision : 2,
35941     /**
35942      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35943      */
35944     allowNegative : true,
35945     
35946     /**
35947      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35948      */
35949     allowZero: true,
35950     /**
35951      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35952      */
35953     minValue : Number.NEGATIVE_INFINITY,
35954     /**
35955      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35956      */
35957     maxValue : Number.MAX_VALUE,
35958     /**
35959      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35960      */
35961     minText : "The minimum value for this field is {0}",
35962     /**
35963      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35964      */
35965     maxText : "The maximum value for this field is {0}",
35966     /**
35967      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35968      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35969      */
35970     nanText : "{0} is not a valid number",
35971     /**
35972      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35973      */
35974     thousandsDelimiter : false,
35975     /**
35976      * @cfg {String} valueAlign alignment of value
35977      */
35978     valueAlign : "left",
35979
35980     getAutoCreate : function()
35981     {
35982         var hiddenInput = {
35983             tag: 'input',
35984             type: 'hidden',
35985             id: Roo.id(),
35986             cls: 'hidden-number-input'
35987         };
35988         
35989         if (this.name) {
35990             hiddenInput.name = this.name;
35991         }
35992         
35993         this.name = '';
35994         
35995         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35996         
35997         this.name = hiddenInput.name;
35998         
35999         if(cfg.cn.length > 0) {
36000             cfg.cn.push(hiddenInput);
36001         }
36002         
36003         return cfg;
36004     },
36005
36006     // private
36007     initEvents : function()
36008     {   
36009         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36010         
36011         var allowed = "0123456789";
36012         
36013         if(this.allowDecimals){
36014             allowed += this.decimalSeparator;
36015         }
36016         
36017         if(this.allowNegative){
36018             allowed += "-";
36019         }
36020         
36021         if(this.thousandsDelimiter) {
36022             allowed += ",";
36023         }
36024         
36025         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36026         
36027         var keyPress = function(e){
36028             
36029             var k = e.getKey();
36030             
36031             var c = e.getCharCode();
36032             
36033             if(
36034                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36035                     allowed.indexOf(String.fromCharCode(c)) === -1
36036             ){
36037                 e.stopEvent();
36038                 return;
36039             }
36040             
36041             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36042                 return;
36043             }
36044             
36045             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36046                 e.stopEvent();
36047             }
36048         };
36049         
36050         this.el.on("keypress", keyPress, this);
36051     },
36052     
36053     validateValue : function(value)
36054     {
36055         
36056         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36057             return false;
36058         }
36059         
36060         var num = this.parseValue(value);
36061         
36062         if(isNaN(num)){
36063             this.markInvalid(String.format(this.nanText, value));
36064             return false;
36065         }
36066         
36067         if(num < this.minValue){
36068             this.markInvalid(String.format(this.minText, this.minValue));
36069             return false;
36070         }
36071         
36072         if(num > this.maxValue){
36073             this.markInvalid(String.format(this.maxText, this.maxValue));
36074             return false;
36075         }
36076         
36077         return true;
36078     },
36079
36080     getValue : function()
36081     {
36082         var v = this.hiddenEl().getValue();
36083         
36084         return this.fixPrecision(this.parseValue(v));
36085     },
36086
36087     parseValue : function(value)
36088     {
36089         if(this.thousandsDelimiter) {
36090             value += "";
36091             r = new RegExp(",", "g");
36092             value = value.replace(r, "");
36093         }
36094         
36095         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36096         return isNaN(value) ? '' : value;
36097     },
36098
36099     fixPrecision : function(value)
36100     {
36101         if(this.thousandsDelimiter) {
36102             value += "";
36103             r = new RegExp(",", "g");
36104             value = value.replace(r, "");
36105         }
36106         
36107         var nan = isNaN(value);
36108         
36109         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36110             return nan ? '' : value;
36111         }
36112         return parseFloat(value).toFixed(this.decimalPrecision);
36113     },
36114
36115     setValue : function(v)
36116     {
36117         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36118         
36119         this.value = v;
36120         
36121         if(this.rendered){
36122             
36123             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36124             
36125             this.inputEl().dom.value = (v == '') ? '' :
36126                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36127             
36128             if(!this.allowZero && v === '0') {
36129                 this.hiddenEl().dom.value = '';
36130                 this.inputEl().dom.value = '';
36131             }
36132             
36133             this.validate();
36134         }
36135     },
36136
36137     decimalPrecisionFcn : function(v)
36138     {
36139         return Math.floor(v);
36140     },
36141
36142     beforeBlur : function()
36143     {
36144         var v = this.parseValue(this.getRawValue());
36145         
36146         if(v || v === 0 || v === ''){
36147             this.setValue(v);
36148         }
36149     },
36150     
36151     hiddenEl : function()
36152     {
36153         return this.el.select('input.hidden-number-input',true).first();
36154     }
36155     
36156 });
36157
36158  
36159
36160 /*
36161 * Licence: LGPL
36162 */
36163
36164 /**
36165  * @class Roo.bootstrap.DocumentSlider
36166  * @extends Roo.bootstrap.Component
36167  * Bootstrap DocumentSlider class
36168  * 
36169  * @constructor
36170  * Create a new DocumentViewer
36171  * @param {Object} config The config object
36172  */
36173
36174 Roo.bootstrap.DocumentSlider = function(config){
36175     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36176     
36177     this.files = [];
36178     
36179     this.addEvents({
36180         /**
36181          * @event initial
36182          * Fire after initEvent
36183          * @param {Roo.bootstrap.DocumentSlider} this
36184          */
36185         "initial" : true,
36186         /**
36187          * @event update
36188          * Fire after update
36189          * @param {Roo.bootstrap.DocumentSlider} this
36190          */
36191         "update" : true,
36192         /**
36193          * @event click
36194          * Fire after click
36195          * @param {Roo.bootstrap.DocumentSlider} this
36196          */
36197         "click" : true
36198     });
36199 };
36200
36201 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36202     
36203     files : false,
36204     
36205     indicator : 0,
36206     
36207     getAutoCreate : function()
36208     {
36209         var cfg = {
36210             tag : 'div',
36211             cls : 'roo-document-slider',
36212             cn : [
36213                 {
36214                     tag : 'div',
36215                     cls : 'roo-document-slider-header',
36216                     cn : [
36217                         {
36218                             tag : 'div',
36219                             cls : 'roo-document-slider-header-title'
36220                         }
36221                     ]
36222                 },
36223                 {
36224                     tag : 'div',
36225                     cls : 'roo-document-slider-body',
36226                     cn : [
36227                         {
36228                             tag : 'div',
36229                             cls : 'roo-document-slider-prev',
36230                             cn : [
36231                                 {
36232                                     tag : 'i',
36233                                     cls : 'fa fa-chevron-left'
36234                                 }
36235                             ]
36236                         },
36237                         {
36238                             tag : 'div',
36239                             cls : 'roo-document-slider-thumb',
36240                             cn : [
36241                                 {
36242                                     tag : 'img',
36243                                     cls : 'roo-document-slider-image'
36244                                 }
36245                             ]
36246                         },
36247                         {
36248                             tag : 'div',
36249                             cls : 'roo-document-slider-next',
36250                             cn : [
36251                                 {
36252                                     tag : 'i',
36253                                     cls : 'fa fa-chevron-right'
36254                                 }
36255                             ]
36256                         }
36257                     ]
36258                 }
36259             ]
36260         };
36261         
36262         return cfg;
36263     },
36264     
36265     initEvents : function()
36266     {
36267         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36268         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36269         
36270         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36271         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36272         
36273         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36274         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36275         
36276         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36277         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36278         
36279         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36280         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36281         
36282         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36283         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36284         
36285         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36286         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36287         
36288         this.thumbEl.on('click', this.onClick, this);
36289         
36290         this.prevIndicator.on('click', this.prev, this);
36291         
36292         this.nextIndicator.on('click', this.next, this);
36293         
36294     },
36295     
36296     initial : function()
36297     {
36298         if(this.files.length){
36299             this.indicator = 1;
36300             this.update()
36301         }
36302         
36303         this.fireEvent('initial', this);
36304     },
36305     
36306     update : function()
36307     {
36308         this.imageEl.attr('src', this.files[this.indicator - 1]);
36309         
36310         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36311         
36312         this.prevIndicator.show();
36313         
36314         if(this.indicator == 1){
36315             this.prevIndicator.hide();
36316         }
36317         
36318         this.nextIndicator.show();
36319         
36320         if(this.indicator == this.files.length){
36321             this.nextIndicator.hide();
36322         }
36323         
36324         this.thumbEl.scrollTo('top');
36325         
36326         this.fireEvent('update', this);
36327     },
36328     
36329     onClick : function(e)
36330     {
36331         e.preventDefault();
36332         
36333         this.fireEvent('click', this);
36334     },
36335     
36336     prev : function(e)
36337     {
36338         e.preventDefault();
36339         
36340         this.indicator = Math.max(1, this.indicator - 1);
36341         
36342         this.update();
36343     },
36344     
36345     next : function(e)
36346     {
36347         e.preventDefault();
36348         
36349         this.indicator = Math.min(this.files.length, this.indicator + 1);
36350         
36351         this.update();
36352     }
36353 });
36354 /*
36355  * - LGPL
36356  *
36357  * RadioSet
36358  *
36359  *
36360  */
36361
36362 /**
36363  * @class Roo.bootstrap.RadioSet
36364  * @extends Roo.bootstrap.Input
36365  * Bootstrap RadioSet class
36366  * @cfg {String} indicatorpos (left|right) default left
36367  * @cfg {Boolean} inline (true|false) inline the element (default true)
36368  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36369  * @constructor
36370  * Create a new RadioSet
36371  * @param {Object} config The config object
36372  */
36373
36374 Roo.bootstrap.RadioSet = function(config){
36375     
36376     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36377     
36378     this.radioes = [];
36379     
36380     Roo.bootstrap.RadioSet.register(this);
36381     
36382     this.addEvents({
36383         /**
36384         * @event check
36385         * Fires when the element is checked or unchecked.
36386         * @param {Roo.bootstrap.RadioSet} this This radio
36387         * @param {Roo.bootstrap.Radio} item The checked item
36388         */
36389        check : true,
36390        /**
36391         * @event click
36392         * Fires when the element is click.
36393         * @param {Roo.bootstrap.RadioSet} this This radio set
36394         * @param {Roo.bootstrap.Radio} item The checked item
36395         * @param {Roo.EventObject} e The event object
36396         */
36397        click : true
36398     });
36399     
36400 };
36401
36402 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36403
36404     radioes : false,
36405     
36406     inline : true,
36407     
36408     weight : '',
36409     
36410     indicatorpos : 'left',
36411     
36412     getAutoCreate : function()
36413     {
36414         var label = {
36415             tag : 'label',
36416             cls : 'roo-radio-set-label',
36417             cn : [
36418                 {
36419                     tag : 'span',
36420                     html : this.fieldLabel
36421                 }
36422             ]
36423         };
36424         if (Roo.bootstrap.version == 3) {
36425             
36426             
36427             if(this.indicatorpos == 'left'){
36428                 label.cn.unshift({
36429                     tag : 'i',
36430                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36431                     tooltip : 'This field is required'
36432                 });
36433             } else {
36434                 label.cn.push({
36435                     tag : 'i',
36436                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36437                     tooltip : 'This field is required'
36438                 });
36439             }
36440         }
36441         var items = {
36442             tag : 'div',
36443             cls : 'roo-radio-set-items'
36444         };
36445         
36446         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36447         
36448         if (align === 'left' && this.fieldLabel.length) {
36449             
36450             items = {
36451                 cls : "roo-radio-set-right", 
36452                 cn: [
36453                     items
36454                 ]
36455             };
36456             
36457             if(this.labelWidth > 12){
36458                 label.style = "width: " + this.labelWidth + 'px';
36459             }
36460             
36461             if(this.labelWidth < 13 && this.labelmd == 0){
36462                 this.labelmd = this.labelWidth;
36463             }
36464             
36465             if(this.labellg > 0){
36466                 label.cls += ' col-lg-' + this.labellg;
36467                 items.cls += ' col-lg-' + (12 - this.labellg);
36468             }
36469             
36470             if(this.labelmd > 0){
36471                 label.cls += ' col-md-' + this.labelmd;
36472                 items.cls += ' col-md-' + (12 - this.labelmd);
36473             }
36474             
36475             if(this.labelsm > 0){
36476                 label.cls += ' col-sm-' + this.labelsm;
36477                 items.cls += ' col-sm-' + (12 - this.labelsm);
36478             }
36479             
36480             if(this.labelxs > 0){
36481                 label.cls += ' col-xs-' + this.labelxs;
36482                 items.cls += ' col-xs-' + (12 - this.labelxs);
36483             }
36484         }
36485         
36486         var cfg = {
36487             tag : 'div',
36488             cls : 'roo-radio-set',
36489             cn : [
36490                 {
36491                     tag : 'input',
36492                     cls : 'roo-radio-set-input',
36493                     type : 'hidden',
36494                     name : this.name,
36495                     value : this.value ? this.value :  ''
36496                 },
36497                 label,
36498                 items
36499             ]
36500         };
36501         
36502         if(this.weight.length){
36503             cfg.cls += ' roo-radio-' + this.weight;
36504         }
36505         
36506         if(this.inline) {
36507             cfg.cls += ' roo-radio-set-inline';
36508         }
36509         
36510         var settings=this;
36511         ['xs','sm','md','lg'].map(function(size){
36512             if (settings[size]) {
36513                 cfg.cls += ' col-' + size + '-' + settings[size];
36514             }
36515         });
36516         
36517         return cfg;
36518         
36519     },
36520
36521     initEvents : function()
36522     {
36523         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36524         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36525         
36526         if(!this.fieldLabel.length){
36527             this.labelEl.hide();
36528         }
36529         
36530         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36531         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36532         
36533         this.indicator = this.indicatorEl();
36534         
36535         if(this.indicator){
36536             this.indicator.addClass('invisible');
36537         }
36538         
36539         this.originalValue = this.getValue();
36540         
36541     },
36542     
36543     inputEl: function ()
36544     {
36545         return this.el.select('.roo-radio-set-input', true).first();
36546     },
36547     
36548     getChildContainer : function()
36549     {
36550         return this.itemsEl;
36551     },
36552     
36553     register : function(item)
36554     {
36555         this.radioes.push(item);
36556         
36557     },
36558     
36559     validate : function()
36560     {   
36561         if(this.getVisibilityEl().hasClass('hidden')){
36562             return true;
36563         }
36564         
36565         var valid = false;
36566         
36567         Roo.each(this.radioes, function(i){
36568             if(!i.checked){
36569                 return;
36570             }
36571             
36572             valid = true;
36573             return false;
36574         });
36575         
36576         if(this.allowBlank) {
36577             return true;
36578         }
36579         
36580         if(this.disabled || valid){
36581             this.markValid();
36582             return true;
36583         }
36584         
36585         this.markInvalid();
36586         return false;
36587         
36588     },
36589     
36590     markValid : function()
36591     {
36592         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36593             this.indicatorEl().removeClass('visible');
36594             this.indicatorEl().addClass('invisible');
36595         }
36596         
36597         
36598         if (Roo.bootstrap.version == 3) {
36599             this.el.removeClass([this.invalidClass, this.validClass]);
36600             this.el.addClass(this.validClass);
36601         } else {
36602             this.el.removeClass(['is-invalid','is-valid']);
36603             this.el.addClass(['is-valid']);
36604         }
36605         this.fireEvent('valid', this);
36606     },
36607     
36608     markInvalid : function(msg)
36609     {
36610         if(this.allowBlank || this.disabled){
36611             return;
36612         }
36613         
36614         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36615             this.indicatorEl().removeClass('invisible');
36616             this.indicatorEl().addClass('visible');
36617         }
36618         if (Roo.bootstrap.version == 3) {
36619             this.el.removeClass([this.invalidClass, this.validClass]);
36620             this.el.addClass(this.invalidClass);
36621         } else {
36622             this.el.removeClass(['is-invalid','is-valid']);
36623             this.el.addClass(['is-invalid']);
36624         }
36625         
36626         this.fireEvent('invalid', this, msg);
36627         
36628     },
36629     
36630     setValue : function(v, suppressEvent)
36631     {   
36632         if(this.value === v){
36633             return;
36634         }
36635         
36636         this.value = v;
36637         
36638         if(this.rendered){
36639             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36640         }
36641         
36642         Roo.each(this.radioes, function(i){
36643             i.checked = false;
36644             i.el.removeClass('checked');
36645         });
36646         
36647         Roo.each(this.radioes, function(i){
36648             
36649             if(i.value === v || i.value.toString() === v.toString()){
36650                 i.checked = true;
36651                 i.el.addClass('checked');
36652                 
36653                 if(suppressEvent !== true){
36654                     this.fireEvent('check', this, i);
36655                 }
36656                 
36657                 return false;
36658             }
36659             
36660         }, this);
36661         
36662         this.validate();
36663     },
36664     
36665     clearInvalid : function(){
36666         
36667         if(!this.el || this.preventMark){
36668             return;
36669         }
36670         
36671         this.el.removeClass([this.invalidClass]);
36672         
36673         this.fireEvent('valid', this);
36674     }
36675     
36676 });
36677
36678 Roo.apply(Roo.bootstrap.RadioSet, {
36679     
36680     groups: {},
36681     
36682     register : function(set)
36683     {
36684         this.groups[set.name] = set;
36685     },
36686     
36687     get: function(name) 
36688     {
36689         if (typeof(this.groups[name]) == 'undefined') {
36690             return false;
36691         }
36692         
36693         return this.groups[name] ;
36694     }
36695     
36696 });
36697 /*
36698  * Based on:
36699  * Ext JS Library 1.1.1
36700  * Copyright(c) 2006-2007, Ext JS, LLC.
36701  *
36702  * Originally Released Under LGPL - original licence link has changed is not relivant.
36703  *
36704  * Fork - LGPL
36705  * <script type="text/javascript">
36706  */
36707
36708
36709 /**
36710  * @class Roo.bootstrap.SplitBar
36711  * @extends Roo.util.Observable
36712  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36713  * <br><br>
36714  * Usage:
36715  * <pre><code>
36716 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36717                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36718 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36719 split.minSize = 100;
36720 split.maxSize = 600;
36721 split.animate = true;
36722 split.on('moved', splitterMoved);
36723 </code></pre>
36724  * @constructor
36725  * Create a new SplitBar
36726  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36727  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36728  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36729  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36730                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36731                         position of the SplitBar).
36732  */
36733 Roo.bootstrap.SplitBar = function(cfg){
36734     
36735     /** @private */
36736     
36737     //{
36738     //  dragElement : elm
36739     //  resizingElement: el,
36740         // optional..
36741     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36742     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36743         // existingProxy ???
36744     //}
36745     
36746     this.el = Roo.get(cfg.dragElement, true);
36747     this.el.dom.unselectable = "on";
36748     /** @private */
36749     this.resizingEl = Roo.get(cfg.resizingElement, true);
36750
36751     /**
36752      * @private
36753      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36754      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36755      * @type Number
36756      */
36757     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36758     
36759     /**
36760      * The minimum size of the resizing element. (Defaults to 0)
36761      * @type Number
36762      */
36763     this.minSize = 0;
36764     
36765     /**
36766      * The maximum size of the resizing element. (Defaults to 2000)
36767      * @type Number
36768      */
36769     this.maxSize = 2000;
36770     
36771     /**
36772      * Whether to animate the transition to the new size
36773      * @type Boolean
36774      */
36775     this.animate = false;
36776     
36777     /**
36778      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36779      * @type Boolean
36780      */
36781     this.useShim = false;
36782     
36783     /** @private */
36784     this.shim = null;
36785     
36786     if(!cfg.existingProxy){
36787         /** @private */
36788         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36789     }else{
36790         this.proxy = Roo.get(cfg.existingProxy).dom;
36791     }
36792     /** @private */
36793     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36794     
36795     /** @private */
36796     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36797     
36798     /** @private */
36799     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36800     
36801     /** @private */
36802     this.dragSpecs = {};
36803     
36804     /**
36805      * @private The adapter to use to positon and resize elements
36806      */
36807     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36808     this.adapter.init(this);
36809     
36810     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36811         /** @private */
36812         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36813         this.el.addClass("roo-splitbar-h");
36814     }else{
36815         /** @private */
36816         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36817         this.el.addClass("roo-splitbar-v");
36818     }
36819     
36820     this.addEvents({
36821         /**
36822          * @event resize
36823          * Fires when the splitter is moved (alias for {@link #event-moved})
36824          * @param {Roo.bootstrap.SplitBar} this
36825          * @param {Number} newSize the new width or height
36826          */
36827         "resize" : true,
36828         /**
36829          * @event moved
36830          * Fires when the splitter is moved
36831          * @param {Roo.bootstrap.SplitBar} this
36832          * @param {Number} newSize the new width or height
36833          */
36834         "moved" : true,
36835         /**
36836          * @event beforeresize
36837          * Fires before the splitter is dragged
36838          * @param {Roo.bootstrap.SplitBar} this
36839          */
36840         "beforeresize" : true,
36841
36842         "beforeapply" : true
36843     });
36844
36845     Roo.util.Observable.call(this);
36846 };
36847
36848 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36849     onStartProxyDrag : function(x, y){
36850         this.fireEvent("beforeresize", this);
36851         if(!this.overlay){
36852             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36853             o.unselectable();
36854             o.enableDisplayMode("block");
36855             // all splitbars share the same overlay
36856             Roo.bootstrap.SplitBar.prototype.overlay = o;
36857         }
36858         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36859         this.overlay.show();
36860         Roo.get(this.proxy).setDisplayed("block");
36861         var size = this.adapter.getElementSize(this);
36862         this.activeMinSize = this.getMinimumSize();;
36863         this.activeMaxSize = this.getMaximumSize();;
36864         var c1 = size - this.activeMinSize;
36865         var c2 = Math.max(this.activeMaxSize - size, 0);
36866         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36867             this.dd.resetConstraints();
36868             this.dd.setXConstraint(
36869                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36870                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36871             );
36872             this.dd.setYConstraint(0, 0);
36873         }else{
36874             this.dd.resetConstraints();
36875             this.dd.setXConstraint(0, 0);
36876             this.dd.setYConstraint(
36877                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36878                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36879             );
36880          }
36881         this.dragSpecs.startSize = size;
36882         this.dragSpecs.startPoint = [x, y];
36883         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36884     },
36885     
36886     /** 
36887      * @private Called after the drag operation by the DDProxy
36888      */
36889     onEndProxyDrag : function(e){
36890         Roo.get(this.proxy).setDisplayed(false);
36891         var endPoint = Roo.lib.Event.getXY(e);
36892         if(this.overlay){
36893             this.overlay.hide();
36894         }
36895         var newSize;
36896         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36897             newSize = this.dragSpecs.startSize + 
36898                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36899                     endPoint[0] - this.dragSpecs.startPoint[0] :
36900                     this.dragSpecs.startPoint[0] - endPoint[0]
36901                 );
36902         }else{
36903             newSize = this.dragSpecs.startSize + 
36904                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36905                     endPoint[1] - this.dragSpecs.startPoint[1] :
36906                     this.dragSpecs.startPoint[1] - endPoint[1]
36907                 );
36908         }
36909         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36910         if(newSize != this.dragSpecs.startSize){
36911             if(this.fireEvent('beforeapply', this, newSize) !== false){
36912                 this.adapter.setElementSize(this, newSize);
36913                 this.fireEvent("moved", this, newSize);
36914                 this.fireEvent("resize", this, newSize);
36915             }
36916         }
36917     },
36918     
36919     /**
36920      * Get the adapter this SplitBar uses
36921      * @return The adapter object
36922      */
36923     getAdapter : function(){
36924         return this.adapter;
36925     },
36926     
36927     /**
36928      * Set the adapter this SplitBar uses
36929      * @param {Object} adapter A SplitBar adapter object
36930      */
36931     setAdapter : function(adapter){
36932         this.adapter = adapter;
36933         this.adapter.init(this);
36934     },
36935     
36936     /**
36937      * Gets the minimum size for the resizing element
36938      * @return {Number} The minimum size
36939      */
36940     getMinimumSize : function(){
36941         return this.minSize;
36942     },
36943     
36944     /**
36945      * Sets the minimum size for the resizing element
36946      * @param {Number} minSize The minimum size
36947      */
36948     setMinimumSize : function(minSize){
36949         this.minSize = minSize;
36950     },
36951     
36952     /**
36953      * Gets the maximum size for the resizing element
36954      * @return {Number} The maximum size
36955      */
36956     getMaximumSize : function(){
36957         return this.maxSize;
36958     },
36959     
36960     /**
36961      * Sets the maximum size for the resizing element
36962      * @param {Number} maxSize The maximum size
36963      */
36964     setMaximumSize : function(maxSize){
36965         this.maxSize = maxSize;
36966     },
36967     
36968     /**
36969      * Sets the initialize size for the resizing element
36970      * @param {Number} size The initial size
36971      */
36972     setCurrentSize : function(size){
36973         var oldAnimate = this.animate;
36974         this.animate = false;
36975         this.adapter.setElementSize(this, size);
36976         this.animate = oldAnimate;
36977     },
36978     
36979     /**
36980      * Destroy this splitbar. 
36981      * @param {Boolean} removeEl True to remove the element
36982      */
36983     destroy : function(removeEl){
36984         if(this.shim){
36985             this.shim.remove();
36986         }
36987         this.dd.unreg();
36988         this.proxy.parentNode.removeChild(this.proxy);
36989         if(removeEl){
36990             this.el.remove();
36991         }
36992     }
36993 });
36994
36995 /**
36996  * @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.
36997  */
36998 Roo.bootstrap.SplitBar.createProxy = function(dir){
36999     var proxy = new Roo.Element(document.createElement("div"));
37000     proxy.unselectable();
37001     var cls = 'roo-splitbar-proxy';
37002     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37003     document.body.appendChild(proxy.dom);
37004     return proxy.dom;
37005 };
37006
37007 /** 
37008  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37009  * Default Adapter. It assumes the splitter and resizing element are not positioned
37010  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37011  */
37012 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37013 };
37014
37015 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37016     // do nothing for now
37017     init : function(s){
37018     
37019     },
37020     /**
37021      * Called before drag operations to get the current size of the resizing element. 
37022      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37023      */
37024      getElementSize : function(s){
37025         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37026             return s.resizingEl.getWidth();
37027         }else{
37028             return s.resizingEl.getHeight();
37029         }
37030     },
37031     
37032     /**
37033      * Called after drag operations to set the size of the resizing element.
37034      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37035      * @param {Number} newSize The new size to set
37036      * @param {Function} onComplete A function to be invoked when resizing is complete
37037      */
37038     setElementSize : function(s, newSize, onComplete){
37039         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37040             if(!s.animate){
37041                 s.resizingEl.setWidth(newSize);
37042                 if(onComplete){
37043                     onComplete(s, newSize);
37044                 }
37045             }else{
37046                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37047             }
37048         }else{
37049             
37050             if(!s.animate){
37051                 s.resizingEl.setHeight(newSize);
37052                 if(onComplete){
37053                     onComplete(s, newSize);
37054                 }
37055             }else{
37056                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37057             }
37058         }
37059     }
37060 };
37061
37062 /** 
37063  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37064  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37065  * Adapter that  moves the splitter element to align with the resized sizing element. 
37066  * Used with an absolute positioned SplitBar.
37067  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37068  * document.body, make sure you assign an id to the body element.
37069  */
37070 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37071     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37072     this.container = Roo.get(container);
37073 };
37074
37075 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37076     init : function(s){
37077         this.basic.init(s);
37078     },
37079     
37080     getElementSize : function(s){
37081         return this.basic.getElementSize(s);
37082     },
37083     
37084     setElementSize : function(s, newSize, onComplete){
37085         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37086     },
37087     
37088     moveSplitter : function(s){
37089         var yes = Roo.bootstrap.SplitBar;
37090         switch(s.placement){
37091             case yes.LEFT:
37092                 s.el.setX(s.resizingEl.getRight());
37093                 break;
37094             case yes.RIGHT:
37095                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37096                 break;
37097             case yes.TOP:
37098                 s.el.setY(s.resizingEl.getBottom());
37099                 break;
37100             case yes.BOTTOM:
37101                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37102                 break;
37103         }
37104     }
37105 };
37106
37107 /**
37108  * Orientation constant - Create a vertical SplitBar
37109  * @static
37110  * @type Number
37111  */
37112 Roo.bootstrap.SplitBar.VERTICAL = 1;
37113
37114 /**
37115  * Orientation constant - Create a horizontal SplitBar
37116  * @static
37117  * @type Number
37118  */
37119 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37120
37121 /**
37122  * Placement constant - The resizing element is to the left of the splitter element
37123  * @static
37124  * @type Number
37125  */
37126 Roo.bootstrap.SplitBar.LEFT = 1;
37127
37128 /**
37129  * Placement constant - The resizing element is to the right of the splitter element
37130  * @static
37131  * @type Number
37132  */
37133 Roo.bootstrap.SplitBar.RIGHT = 2;
37134
37135 /**
37136  * Placement constant - The resizing element is positioned above the splitter element
37137  * @static
37138  * @type Number
37139  */
37140 Roo.bootstrap.SplitBar.TOP = 3;
37141
37142 /**
37143  * Placement constant - The resizing element is positioned under splitter element
37144  * @static
37145  * @type Number
37146  */
37147 Roo.bootstrap.SplitBar.BOTTOM = 4;
37148 Roo.namespace("Roo.bootstrap.layout");/*
37149  * Based on:
37150  * Ext JS Library 1.1.1
37151  * Copyright(c) 2006-2007, Ext JS, LLC.
37152  *
37153  * Originally Released Under LGPL - original licence link has changed is not relivant.
37154  *
37155  * Fork - LGPL
37156  * <script type="text/javascript">
37157  */
37158
37159 /**
37160  * @class Roo.bootstrap.layout.Manager
37161  * @extends Roo.bootstrap.Component
37162  * Base class for layout managers.
37163  */
37164 Roo.bootstrap.layout.Manager = function(config)
37165 {
37166     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37167
37168
37169
37170
37171
37172     /** false to disable window resize monitoring @type Boolean */
37173     this.monitorWindowResize = true;
37174     this.regions = {};
37175     this.addEvents({
37176         /**
37177          * @event layout
37178          * Fires when a layout is performed.
37179          * @param {Roo.LayoutManager} this
37180          */
37181         "layout" : true,
37182         /**
37183          * @event regionresized
37184          * Fires when the user resizes a region.
37185          * @param {Roo.LayoutRegion} region The resized region
37186          * @param {Number} newSize The new size (width for east/west, height for north/south)
37187          */
37188         "regionresized" : true,
37189         /**
37190          * @event regioncollapsed
37191          * Fires when a region is collapsed.
37192          * @param {Roo.LayoutRegion} region The collapsed region
37193          */
37194         "regioncollapsed" : true,
37195         /**
37196          * @event regionexpanded
37197          * Fires when a region is expanded.
37198          * @param {Roo.LayoutRegion} region The expanded region
37199          */
37200         "regionexpanded" : true
37201     });
37202     this.updating = false;
37203
37204     if (config.el) {
37205         this.el = Roo.get(config.el);
37206         this.initEvents();
37207     }
37208
37209 };
37210
37211 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37212
37213
37214     regions : null,
37215
37216     monitorWindowResize : true,
37217
37218
37219     updating : false,
37220
37221
37222     onRender : function(ct, position)
37223     {
37224         if(!this.el){
37225             this.el = Roo.get(ct);
37226             this.initEvents();
37227         }
37228         //this.fireEvent('render',this);
37229     },
37230
37231
37232     initEvents: function()
37233     {
37234
37235
37236         // ie scrollbar fix
37237         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37238             document.body.scroll = "no";
37239         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37240             this.el.position('relative');
37241         }
37242         this.id = this.el.id;
37243         this.el.addClass("roo-layout-container");
37244         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37245         if(this.el.dom != document.body ) {
37246             this.el.on('resize', this.layout,this);
37247             this.el.on('show', this.layout,this);
37248         }
37249
37250     },
37251
37252     /**
37253      * Returns true if this layout is currently being updated
37254      * @return {Boolean}
37255      */
37256     isUpdating : function(){
37257         return this.updating;
37258     },
37259
37260     /**
37261      * Suspend the LayoutManager from doing auto-layouts while
37262      * making multiple add or remove calls
37263      */
37264     beginUpdate : function(){
37265         this.updating = true;
37266     },
37267
37268     /**
37269      * Restore auto-layouts and optionally disable the manager from performing a layout
37270      * @param {Boolean} noLayout true to disable a layout update
37271      */
37272     endUpdate : function(noLayout){
37273         this.updating = false;
37274         if(!noLayout){
37275             this.layout();
37276         }
37277     },
37278
37279     layout: function(){
37280         // abstract...
37281     },
37282
37283     onRegionResized : function(region, newSize){
37284         this.fireEvent("regionresized", region, newSize);
37285         this.layout();
37286     },
37287
37288     onRegionCollapsed : function(region){
37289         this.fireEvent("regioncollapsed", region);
37290     },
37291
37292     onRegionExpanded : function(region){
37293         this.fireEvent("regionexpanded", region);
37294     },
37295
37296     /**
37297      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37298      * performs box-model adjustments.
37299      * @return {Object} The size as an object {width: (the width), height: (the height)}
37300      */
37301     getViewSize : function()
37302     {
37303         var size;
37304         if(this.el.dom != document.body){
37305             size = this.el.getSize();
37306         }else{
37307             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37308         }
37309         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37310         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37311         return size;
37312     },
37313
37314     /**
37315      * Returns the Element this layout is bound to.
37316      * @return {Roo.Element}
37317      */
37318     getEl : function(){
37319         return this.el;
37320     },
37321
37322     /**
37323      * Returns the specified region.
37324      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37325      * @return {Roo.LayoutRegion}
37326      */
37327     getRegion : function(target){
37328         return this.regions[target.toLowerCase()];
37329     },
37330
37331     onWindowResize : function(){
37332         if(this.monitorWindowResize){
37333             this.layout();
37334         }
37335     }
37336 });
37337 /*
37338  * Based on:
37339  * Ext JS Library 1.1.1
37340  * Copyright(c) 2006-2007, Ext JS, LLC.
37341  *
37342  * Originally Released Under LGPL - original licence link has changed is not relivant.
37343  *
37344  * Fork - LGPL
37345  * <script type="text/javascript">
37346  */
37347 /**
37348  * @class Roo.bootstrap.layout.Border
37349  * @extends Roo.bootstrap.layout.Manager
37350  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37351  * please see: examples/bootstrap/nested.html<br><br>
37352  
37353 <b>The container the layout is rendered into can be either the body element or any other element.
37354 If it is not the body element, the container needs to either be an absolute positioned element,
37355 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37356 the container size if it is not the body element.</b>
37357
37358 * @constructor
37359 * Create a new Border
37360 * @param {Object} config Configuration options
37361  */
37362 Roo.bootstrap.layout.Border = function(config){
37363     config = config || {};
37364     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37365     
37366     
37367     
37368     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37369         if(config[region]){
37370             config[region].region = region;
37371             this.addRegion(config[region]);
37372         }
37373     },this);
37374     
37375 };
37376
37377 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37378
37379 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37380     
37381     parent : false, // this might point to a 'nest' or a ???
37382     
37383     /**
37384      * Creates and adds a new region if it doesn't already exist.
37385      * @param {String} target The target region key (north, south, east, west or center).
37386      * @param {Object} config The regions config object
37387      * @return {BorderLayoutRegion} The new region
37388      */
37389     addRegion : function(config)
37390     {
37391         if(!this.regions[config.region]){
37392             var r = this.factory(config);
37393             this.bindRegion(r);
37394         }
37395         return this.regions[config.region];
37396     },
37397
37398     // private (kinda)
37399     bindRegion : function(r){
37400         this.regions[r.config.region] = r;
37401         
37402         r.on("visibilitychange",    this.layout, this);
37403         r.on("paneladded",          this.layout, this);
37404         r.on("panelremoved",        this.layout, this);
37405         r.on("invalidated",         this.layout, this);
37406         r.on("resized",             this.onRegionResized, this);
37407         r.on("collapsed",           this.onRegionCollapsed, this);
37408         r.on("expanded",            this.onRegionExpanded, this);
37409     },
37410
37411     /**
37412      * Performs a layout update.
37413      */
37414     layout : function()
37415     {
37416         if(this.updating) {
37417             return;
37418         }
37419         
37420         // render all the rebions if they have not been done alreayd?
37421         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37422             if(this.regions[region] && !this.regions[region].bodyEl){
37423                 this.regions[region].onRender(this.el)
37424             }
37425         },this);
37426         
37427         var size = this.getViewSize();
37428         var w = size.width;
37429         var h = size.height;
37430         var centerW = w;
37431         var centerH = h;
37432         var centerY = 0;
37433         var centerX = 0;
37434         //var x = 0, y = 0;
37435
37436         var rs = this.regions;
37437         var north = rs["north"];
37438         var south = rs["south"]; 
37439         var west = rs["west"];
37440         var east = rs["east"];
37441         var center = rs["center"];
37442         //if(this.hideOnLayout){ // not supported anymore
37443             //c.el.setStyle("display", "none");
37444         //}
37445         if(north && north.isVisible()){
37446             var b = north.getBox();
37447             var m = north.getMargins();
37448             b.width = w - (m.left+m.right);
37449             b.x = m.left;
37450             b.y = m.top;
37451             centerY = b.height + b.y + m.bottom;
37452             centerH -= centerY;
37453             north.updateBox(this.safeBox(b));
37454         }
37455         if(south && south.isVisible()){
37456             var b = south.getBox();
37457             var m = south.getMargins();
37458             b.width = w - (m.left+m.right);
37459             b.x = m.left;
37460             var totalHeight = (b.height + m.top + m.bottom);
37461             b.y = h - totalHeight + m.top;
37462             centerH -= totalHeight;
37463             south.updateBox(this.safeBox(b));
37464         }
37465         if(west && west.isVisible()){
37466             var b = west.getBox();
37467             var m = west.getMargins();
37468             b.height = centerH - (m.top+m.bottom);
37469             b.x = m.left;
37470             b.y = centerY + m.top;
37471             var totalWidth = (b.width + m.left + m.right);
37472             centerX += totalWidth;
37473             centerW -= totalWidth;
37474             west.updateBox(this.safeBox(b));
37475         }
37476         if(east && east.isVisible()){
37477             var b = east.getBox();
37478             var m = east.getMargins();
37479             b.height = centerH - (m.top+m.bottom);
37480             var totalWidth = (b.width + m.left + m.right);
37481             b.x = w - totalWidth + m.left;
37482             b.y = centerY + m.top;
37483             centerW -= totalWidth;
37484             east.updateBox(this.safeBox(b));
37485         }
37486         if(center){
37487             var m = center.getMargins();
37488             var centerBox = {
37489                 x: centerX + m.left,
37490                 y: centerY + m.top,
37491                 width: centerW - (m.left+m.right),
37492                 height: centerH - (m.top+m.bottom)
37493             };
37494             //if(this.hideOnLayout){
37495                 //center.el.setStyle("display", "block");
37496             //}
37497             center.updateBox(this.safeBox(centerBox));
37498         }
37499         this.el.repaint();
37500         this.fireEvent("layout", this);
37501     },
37502
37503     // private
37504     safeBox : function(box){
37505         box.width = Math.max(0, box.width);
37506         box.height = Math.max(0, box.height);
37507         return box;
37508     },
37509
37510     /**
37511      * Adds a ContentPanel (or subclass) to this layout.
37512      * @param {String} target The target region key (north, south, east, west or center).
37513      * @param {Roo.ContentPanel} panel The panel to add
37514      * @return {Roo.ContentPanel} The added panel
37515      */
37516     add : function(target, panel){
37517          
37518         target = target.toLowerCase();
37519         return this.regions[target].add(panel);
37520     },
37521
37522     /**
37523      * Remove a ContentPanel (or subclass) to this layout.
37524      * @param {String} target The target region key (north, south, east, west or center).
37525      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37526      * @return {Roo.ContentPanel} The removed panel
37527      */
37528     remove : function(target, panel){
37529         target = target.toLowerCase();
37530         return this.regions[target].remove(panel);
37531     },
37532
37533     /**
37534      * Searches all regions for a panel with the specified id
37535      * @param {String} panelId
37536      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37537      */
37538     findPanel : function(panelId){
37539         var rs = this.regions;
37540         for(var target in rs){
37541             if(typeof rs[target] != "function"){
37542                 var p = rs[target].getPanel(panelId);
37543                 if(p){
37544                     return p;
37545                 }
37546             }
37547         }
37548         return null;
37549     },
37550
37551     /**
37552      * Searches all regions for a panel with the specified id and activates (shows) it.
37553      * @param {String/ContentPanel} panelId The panels id or the panel itself
37554      * @return {Roo.ContentPanel} The shown panel or null
37555      */
37556     showPanel : function(panelId) {
37557       var rs = this.regions;
37558       for(var target in rs){
37559          var r = rs[target];
37560          if(typeof r != "function"){
37561             if(r.hasPanel(panelId)){
37562                return r.showPanel(panelId);
37563             }
37564          }
37565       }
37566       return null;
37567    },
37568
37569    /**
37570      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37571      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37572      */
37573    /*
37574     restoreState : function(provider){
37575         if(!provider){
37576             provider = Roo.state.Manager;
37577         }
37578         var sm = new Roo.LayoutStateManager();
37579         sm.init(this, provider);
37580     },
37581 */
37582  
37583  
37584     /**
37585      * Adds a xtype elements to the layout.
37586      * <pre><code>
37587
37588 layout.addxtype({
37589        xtype : 'ContentPanel',
37590        region: 'west',
37591        items: [ .... ]
37592    }
37593 );
37594
37595 layout.addxtype({
37596         xtype : 'NestedLayoutPanel',
37597         region: 'west',
37598         layout: {
37599            center: { },
37600            west: { }   
37601         },
37602         items : [ ... list of content panels or nested layout panels.. ]
37603    }
37604 );
37605 </code></pre>
37606      * @param {Object} cfg Xtype definition of item to add.
37607      */
37608     addxtype : function(cfg)
37609     {
37610         // basically accepts a pannel...
37611         // can accept a layout region..!?!?
37612         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37613         
37614         
37615         // theory?  children can only be panels??
37616         
37617         //if (!cfg.xtype.match(/Panel$/)) {
37618         //    return false;
37619         //}
37620         var ret = false;
37621         
37622         if (typeof(cfg.region) == 'undefined') {
37623             Roo.log("Failed to add Panel, region was not set");
37624             Roo.log(cfg);
37625             return false;
37626         }
37627         var region = cfg.region;
37628         delete cfg.region;
37629         
37630           
37631         var xitems = [];
37632         if (cfg.items) {
37633             xitems = cfg.items;
37634             delete cfg.items;
37635         }
37636         var nb = false;
37637         
37638         if ( region == 'center') {
37639             Roo.log("Center: " + cfg.title);
37640         }
37641         
37642         
37643         switch(cfg.xtype) 
37644         {
37645             case 'Content':  // ContentPanel (el, cfg)
37646             case 'Scroll':  // ContentPanel (el, cfg)
37647             case 'View': 
37648                 cfg.autoCreate = cfg.autoCreate || true;
37649                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37650                 //} else {
37651                 //    var el = this.el.createChild();
37652                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37653                 //}
37654                 
37655                 this.add(region, ret);
37656                 break;
37657             
37658             /*
37659             case 'TreePanel': // our new panel!
37660                 cfg.el = this.el.createChild();
37661                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37662                 this.add(region, ret);
37663                 break;
37664             */
37665             
37666             case 'Nest': 
37667                 // create a new Layout (which is  a Border Layout...
37668                 
37669                 var clayout = cfg.layout;
37670                 clayout.el  = this.el.createChild();
37671                 clayout.items   = clayout.items  || [];
37672                 
37673                 delete cfg.layout;
37674                 
37675                 // replace this exitems with the clayout ones..
37676                 xitems = clayout.items;
37677                  
37678                 // force background off if it's in center...
37679                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37680                     cfg.background = false;
37681                 }
37682                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37683                 
37684                 
37685                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37686                 //console.log('adding nested layout panel '  + cfg.toSource());
37687                 this.add(region, ret);
37688                 nb = {}; /// find first...
37689                 break;
37690             
37691             case 'Grid':
37692                 
37693                 // needs grid and region
37694                 
37695                 //var el = this.getRegion(region).el.createChild();
37696                 /*
37697                  *var el = this.el.createChild();
37698                 // create the grid first...
37699                 cfg.grid.container = el;
37700                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37701                 */
37702                 
37703                 if (region == 'center' && this.active ) {
37704                     cfg.background = false;
37705                 }
37706                 
37707                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37708                 
37709                 this.add(region, ret);
37710                 /*
37711                 if (cfg.background) {
37712                     // render grid on panel activation (if panel background)
37713                     ret.on('activate', function(gp) {
37714                         if (!gp.grid.rendered) {
37715                     //        gp.grid.render(el);
37716                         }
37717                     });
37718                 } else {
37719                   //  cfg.grid.render(el);
37720                 }
37721                 */
37722                 break;
37723            
37724            
37725             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37726                 // it was the old xcomponent building that caused this before.
37727                 // espeically if border is the top element in the tree.
37728                 ret = this;
37729                 break; 
37730                 
37731                     
37732                 
37733                 
37734                 
37735             default:
37736                 /*
37737                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37738                     
37739                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37740                     this.add(region, ret);
37741                 } else {
37742                 */
37743                     Roo.log(cfg);
37744                     throw "Can not add '" + cfg.xtype + "' to Border";
37745                     return null;
37746              
37747                                 
37748              
37749         }
37750         this.beginUpdate();
37751         // add children..
37752         var region = '';
37753         var abn = {};
37754         Roo.each(xitems, function(i)  {
37755             region = nb && i.region ? i.region : false;
37756             
37757             var add = ret.addxtype(i);
37758            
37759             if (region) {
37760                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37761                 if (!i.background) {
37762                     abn[region] = nb[region] ;
37763                 }
37764             }
37765             
37766         });
37767         this.endUpdate();
37768
37769         // make the last non-background panel active..
37770         //if (nb) { Roo.log(abn); }
37771         if (nb) {
37772             
37773             for(var r in abn) {
37774                 region = this.getRegion(r);
37775                 if (region) {
37776                     // tried using nb[r], but it does not work..
37777                      
37778                     region.showPanel(abn[r]);
37779                    
37780                 }
37781             }
37782         }
37783         return ret;
37784         
37785     },
37786     
37787     
37788 // private
37789     factory : function(cfg)
37790     {
37791         
37792         var validRegions = Roo.bootstrap.layout.Border.regions;
37793
37794         var target = cfg.region;
37795         cfg.mgr = this;
37796         
37797         var r = Roo.bootstrap.layout;
37798         Roo.log(target);
37799         switch(target){
37800             case "north":
37801                 return new r.North(cfg);
37802             case "south":
37803                 return new r.South(cfg);
37804             case "east":
37805                 return new r.East(cfg);
37806             case "west":
37807                 return new r.West(cfg);
37808             case "center":
37809                 return new r.Center(cfg);
37810         }
37811         throw 'Layout region "'+target+'" not supported.';
37812     }
37813     
37814     
37815 });
37816  /*
37817  * Based on:
37818  * Ext JS Library 1.1.1
37819  * Copyright(c) 2006-2007, Ext JS, LLC.
37820  *
37821  * Originally Released Under LGPL - original licence link has changed is not relivant.
37822  *
37823  * Fork - LGPL
37824  * <script type="text/javascript">
37825  */
37826  
37827 /**
37828  * @class Roo.bootstrap.layout.Basic
37829  * @extends Roo.util.Observable
37830  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37831  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37832  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37833  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37834  * @cfg {string}   region  the region that it inhabits..
37835  * @cfg {bool}   skipConfig skip config?
37836  * 
37837
37838  */
37839 Roo.bootstrap.layout.Basic = function(config){
37840     
37841     this.mgr = config.mgr;
37842     
37843     this.position = config.region;
37844     
37845     var skipConfig = config.skipConfig;
37846     
37847     this.events = {
37848         /**
37849          * @scope Roo.BasicLayoutRegion
37850          */
37851         
37852         /**
37853          * @event beforeremove
37854          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37855          * @param {Roo.LayoutRegion} this
37856          * @param {Roo.ContentPanel} panel The panel
37857          * @param {Object} e The cancel event object
37858          */
37859         "beforeremove" : true,
37860         /**
37861          * @event invalidated
37862          * Fires when the layout for this region is changed.
37863          * @param {Roo.LayoutRegion} this
37864          */
37865         "invalidated" : true,
37866         /**
37867          * @event visibilitychange
37868          * Fires when this region is shown or hidden 
37869          * @param {Roo.LayoutRegion} this
37870          * @param {Boolean} visibility true or false
37871          */
37872         "visibilitychange" : true,
37873         /**
37874          * @event paneladded
37875          * Fires when a panel is added. 
37876          * @param {Roo.LayoutRegion} this
37877          * @param {Roo.ContentPanel} panel The panel
37878          */
37879         "paneladded" : true,
37880         /**
37881          * @event panelremoved
37882          * Fires when a panel is removed. 
37883          * @param {Roo.LayoutRegion} this
37884          * @param {Roo.ContentPanel} panel The panel
37885          */
37886         "panelremoved" : true,
37887         /**
37888          * @event beforecollapse
37889          * Fires when this region before collapse.
37890          * @param {Roo.LayoutRegion} this
37891          */
37892         "beforecollapse" : true,
37893         /**
37894          * @event collapsed
37895          * Fires when this region is collapsed.
37896          * @param {Roo.LayoutRegion} this
37897          */
37898         "collapsed" : true,
37899         /**
37900          * @event expanded
37901          * Fires when this region is expanded.
37902          * @param {Roo.LayoutRegion} this
37903          */
37904         "expanded" : true,
37905         /**
37906          * @event slideshow
37907          * Fires when this region is slid into view.
37908          * @param {Roo.LayoutRegion} this
37909          */
37910         "slideshow" : true,
37911         /**
37912          * @event slidehide
37913          * Fires when this region slides out of view. 
37914          * @param {Roo.LayoutRegion} this
37915          */
37916         "slidehide" : true,
37917         /**
37918          * @event panelactivated
37919          * Fires when a panel is activated. 
37920          * @param {Roo.LayoutRegion} this
37921          * @param {Roo.ContentPanel} panel The activated panel
37922          */
37923         "panelactivated" : true,
37924         /**
37925          * @event resized
37926          * Fires when the user resizes this region. 
37927          * @param {Roo.LayoutRegion} this
37928          * @param {Number} newSize The new size (width for east/west, height for north/south)
37929          */
37930         "resized" : true
37931     };
37932     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37933     this.panels = new Roo.util.MixedCollection();
37934     this.panels.getKey = this.getPanelId.createDelegate(this);
37935     this.box = null;
37936     this.activePanel = null;
37937     // ensure listeners are added...
37938     
37939     if (config.listeners || config.events) {
37940         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37941             listeners : config.listeners || {},
37942             events : config.events || {}
37943         });
37944     }
37945     
37946     if(skipConfig !== true){
37947         this.applyConfig(config);
37948     }
37949 };
37950
37951 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37952 {
37953     getPanelId : function(p){
37954         return p.getId();
37955     },
37956     
37957     applyConfig : function(config){
37958         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37959         this.config = config;
37960         
37961     },
37962     
37963     /**
37964      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37965      * the width, for horizontal (north, south) the height.
37966      * @param {Number} newSize The new width or height
37967      */
37968     resizeTo : function(newSize){
37969         var el = this.el ? this.el :
37970                  (this.activePanel ? this.activePanel.getEl() : null);
37971         if(el){
37972             switch(this.position){
37973                 case "east":
37974                 case "west":
37975                     el.setWidth(newSize);
37976                     this.fireEvent("resized", this, newSize);
37977                 break;
37978                 case "north":
37979                 case "south":
37980                     el.setHeight(newSize);
37981                     this.fireEvent("resized", this, newSize);
37982                 break;                
37983             }
37984         }
37985     },
37986     
37987     getBox : function(){
37988         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37989     },
37990     
37991     getMargins : function(){
37992         return this.margins;
37993     },
37994     
37995     updateBox : function(box){
37996         this.box = box;
37997         var el = this.activePanel.getEl();
37998         el.dom.style.left = box.x + "px";
37999         el.dom.style.top = box.y + "px";
38000         this.activePanel.setSize(box.width, box.height);
38001     },
38002     
38003     /**
38004      * Returns the container element for this region.
38005      * @return {Roo.Element}
38006      */
38007     getEl : function(){
38008         return this.activePanel;
38009     },
38010     
38011     /**
38012      * Returns true if this region is currently visible.
38013      * @return {Boolean}
38014      */
38015     isVisible : function(){
38016         return this.activePanel ? true : false;
38017     },
38018     
38019     setActivePanel : function(panel){
38020         panel = this.getPanel(panel);
38021         if(this.activePanel && this.activePanel != panel){
38022             this.activePanel.setActiveState(false);
38023             this.activePanel.getEl().setLeftTop(-10000,-10000);
38024         }
38025         this.activePanel = panel;
38026         panel.setActiveState(true);
38027         if(this.box){
38028             panel.setSize(this.box.width, this.box.height);
38029         }
38030         this.fireEvent("panelactivated", this, panel);
38031         this.fireEvent("invalidated");
38032     },
38033     
38034     /**
38035      * Show the specified panel.
38036      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38037      * @return {Roo.ContentPanel} The shown panel or null
38038      */
38039     showPanel : function(panel){
38040         panel = this.getPanel(panel);
38041         if(panel){
38042             this.setActivePanel(panel);
38043         }
38044         return panel;
38045     },
38046     
38047     /**
38048      * Get the active panel for this region.
38049      * @return {Roo.ContentPanel} The active panel or null
38050      */
38051     getActivePanel : function(){
38052         return this.activePanel;
38053     },
38054     
38055     /**
38056      * Add the passed ContentPanel(s)
38057      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38058      * @return {Roo.ContentPanel} The panel added (if only one was added)
38059      */
38060     add : function(panel){
38061         if(arguments.length > 1){
38062             for(var i = 0, len = arguments.length; i < len; i++) {
38063                 this.add(arguments[i]);
38064             }
38065             return null;
38066         }
38067         if(this.hasPanel(panel)){
38068             this.showPanel(panel);
38069             return panel;
38070         }
38071         var el = panel.getEl();
38072         if(el.dom.parentNode != this.mgr.el.dom){
38073             this.mgr.el.dom.appendChild(el.dom);
38074         }
38075         if(panel.setRegion){
38076             panel.setRegion(this);
38077         }
38078         this.panels.add(panel);
38079         el.setStyle("position", "absolute");
38080         if(!panel.background){
38081             this.setActivePanel(panel);
38082             if(this.config.initialSize && this.panels.getCount()==1){
38083                 this.resizeTo(this.config.initialSize);
38084             }
38085         }
38086         this.fireEvent("paneladded", this, panel);
38087         return panel;
38088     },
38089     
38090     /**
38091      * Returns true if the panel is in this region.
38092      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38093      * @return {Boolean}
38094      */
38095     hasPanel : function(panel){
38096         if(typeof panel == "object"){ // must be panel obj
38097             panel = panel.getId();
38098         }
38099         return this.getPanel(panel) ? true : false;
38100     },
38101     
38102     /**
38103      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38104      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38105      * @param {Boolean} preservePanel Overrides the config preservePanel option
38106      * @return {Roo.ContentPanel} The panel that was removed
38107      */
38108     remove : function(panel, preservePanel){
38109         panel = this.getPanel(panel);
38110         if(!panel){
38111             return null;
38112         }
38113         var e = {};
38114         this.fireEvent("beforeremove", this, panel, e);
38115         if(e.cancel === true){
38116             return null;
38117         }
38118         var panelId = panel.getId();
38119         this.panels.removeKey(panelId);
38120         return panel;
38121     },
38122     
38123     /**
38124      * Returns the panel specified or null if it's not in this region.
38125      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38126      * @return {Roo.ContentPanel}
38127      */
38128     getPanel : function(id){
38129         if(typeof id == "object"){ // must be panel obj
38130             return id;
38131         }
38132         return this.panels.get(id);
38133     },
38134     
38135     /**
38136      * Returns this regions position (north/south/east/west/center).
38137      * @return {String} 
38138      */
38139     getPosition: function(){
38140         return this.position;    
38141     }
38142 });/*
38143  * Based on:
38144  * Ext JS Library 1.1.1
38145  * Copyright(c) 2006-2007, Ext JS, LLC.
38146  *
38147  * Originally Released Under LGPL - original licence link has changed is not relivant.
38148  *
38149  * Fork - LGPL
38150  * <script type="text/javascript">
38151  */
38152  
38153 /**
38154  * @class Roo.bootstrap.layout.Region
38155  * @extends Roo.bootstrap.layout.Basic
38156  * This class represents a region in a layout manager.
38157  
38158  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38159  * @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})
38160  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38161  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38162  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38163  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38164  * @cfg {String}    title           The title for the region (overrides panel titles)
38165  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38166  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38167  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38168  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38169  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38170  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38171  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38172  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38173  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38174  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38175
38176  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38177  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38178  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38179  * @cfg {Number}    width           For East/West panels
38180  * @cfg {Number}    height          For North/South panels
38181  * @cfg {Boolean}   split           To show the splitter
38182  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38183  * 
38184  * @cfg {string}   cls             Extra CSS classes to add to region
38185  * 
38186  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38187  * @cfg {string}   region  the region that it inhabits..
38188  *
38189
38190  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38191  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38192
38193  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38194  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38195  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38196  */
38197 Roo.bootstrap.layout.Region = function(config)
38198 {
38199     this.applyConfig(config);
38200
38201     var mgr = config.mgr;
38202     var pos = config.region;
38203     config.skipConfig = true;
38204     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38205     
38206     if (mgr.el) {
38207         this.onRender(mgr.el);   
38208     }
38209      
38210     this.visible = true;
38211     this.collapsed = false;
38212     this.unrendered_panels = [];
38213 };
38214
38215 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38216
38217     position: '', // set by wrapper (eg. north/south etc..)
38218     unrendered_panels : null,  // unrendered panels.
38219     
38220     tabPosition : false,
38221     
38222     mgr: false, // points to 'Border'
38223     
38224     
38225     createBody : function(){
38226         /** This region's body element 
38227         * @type Roo.Element */
38228         this.bodyEl = this.el.createChild({
38229                 tag: "div",
38230                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38231         });
38232     },
38233
38234     onRender: function(ctr, pos)
38235     {
38236         var dh = Roo.DomHelper;
38237         /** This region's container element 
38238         * @type Roo.Element */
38239         this.el = dh.append(ctr.dom, {
38240                 tag: "div",
38241                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38242             }, true);
38243         /** This region's title element 
38244         * @type Roo.Element */
38245     
38246         this.titleEl = dh.append(this.el.dom,  {
38247                 tag: "div",
38248                 unselectable: "on",
38249                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38250                 children:[
38251                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38252                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38253                 ]
38254             }, true);
38255         
38256         this.titleEl.enableDisplayMode();
38257         /** This region's title text element 
38258         * @type HTMLElement */
38259         this.titleTextEl = this.titleEl.dom.firstChild;
38260         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38261         /*
38262         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38263         this.closeBtn.enableDisplayMode();
38264         this.closeBtn.on("click", this.closeClicked, this);
38265         this.closeBtn.hide();
38266     */
38267         this.createBody(this.config);
38268         if(this.config.hideWhenEmpty){
38269             this.hide();
38270             this.on("paneladded", this.validateVisibility, this);
38271             this.on("panelremoved", this.validateVisibility, this);
38272         }
38273         if(this.autoScroll){
38274             this.bodyEl.setStyle("overflow", "auto");
38275         }else{
38276             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38277         }
38278         //if(c.titlebar !== false){
38279             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38280                 this.titleEl.hide();
38281             }else{
38282                 this.titleEl.show();
38283                 if(this.config.title){
38284                     this.titleTextEl.innerHTML = this.config.title;
38285                 }
38286             }
38287         //}
38288         if(this.config.collapsed){
38289             this.collapse(true);
38290         }
38291         if(this.config.hidden){
38292             this.hide();
38293         }
38294         
38295         if (this.unrendered_panels && this.unrendered_panels.length) {
38296             for (var i =0;i< this.unrendered_panels.length; i++) {
38297                 this.add(this.unrendered_panels[i]);
38298             }
38299             this.unrendered_panels = null;
38300             
38301         }
38302         
38303     },
38304     
38305     applyConfig : function(c)
38306     {
38307         /*
38308          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38309             var dh = Roo.DomHelper;
38310             if(c.titlebar !== false){
38311                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38312                 this.collapseBtn.on("click", this.collapse, this);
38313                 this.collapseBtn.enableDisplayMode();
38314                 /*
38315                 if(c.showPin === true || this.showPin){
38316                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38317                     this.stickBtn.enableDisplayMode();
38318                     this.stickBtn.on("click", this.expand, this);
38319                     this.stickBtn.hide();
38320                 }
38321                 
38322             }
38323             */
38324             /** This region's collapsed element
38325             * @type Roo.Element */
38326             /*
38327              *
38328             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38329                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38330             ]}, true);
38331             
38332             if(c.floatable !== false){
38333                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38334                this.collapsedEl.on("click", this.collapseClick, this);
38335             }
38336
38337             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38338                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38339                    id: "message", unselectable: "on", style:{"float":"left"}});
38340                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38341              }
38342             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38343             this.expandBtn.on("click", this.expand, this);
38344             
38345         }
38346         
38347         if(this.collapseBtn){
38348             this.collapseBtn.setVisible(c.collapsible == true);
38349         }
38350         
38351         this.cmargins = c.cmargins || this.cmargins ||
38352                          (this.position == "west" || this.position == "east" ?
38353                              {top: 0, left: 2, right:2, bottom: 0} :
38354                              {top: 2, left: 0, right:0, bottom: 2});
38355         */
38356         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38357         
38358         
38359         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38360         
38361         this.autoScroll = c.autoScroll || false;
38362         
38363         
38364        
38365         
38366         this.duration = c.duration || .30;
38367         this.slideDuration = c.slideDuration || .45;
38368         this.config = c;
38369        
38370     },
38371     /**
38372      * Returns true if this region is currently visible.
38373      * @return {Boolean}
38374      */
38375     isVisible : function(){
38376         return this.visible;
38377     },
38378
38379     /**
38380      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38381      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38382      */
38383     //setCollapsedTitle : function(title){
38384     //    title = title || "&#160;";
38385      //   if(this.collapsedTitleTextEl){
38386       //      this.collapsedTitleTextEl.innerHTML = title;
38387        // }
38388     //},
38389
38390     getBox : function(){
38391         var b;
38392       //  if(!this.collapsed){
38393             b = this.el.getBox(false, true);
38394        // }else{
38395           //  b = this.collapsedEl.getBox(false, true);
38396         //}
38397         return b;
38398     },
38399
38400     getMargins : function(){
38401         return this.margins;
38402         //return this.collapsed ? this.cmargins : this.margins;
38403     },
38404 /*
38405     highlight : function(){
38406         this.el.addClass("x-layout-panel-dragover");
38407     },
38408
38409     unhighlight : function(){
38410         this.el.removeClass("x-layout-panel-dragover");
38411     },
38412 */
38413     updateBox : function(box)
38414     {
38415         if (!this.bodyEl) {
38416             return; // not rendered yet..
38417         }
38418         
38419         this.box = box;
38420         if(!this.collapsed){
38421             this.el.dom.style.left = box.x + "px";
38422             this.el.dom.style.top = box.y + "px";
38423             this.updateBody(box.width, box.height);
38424         }else{
38425             this.collapsedEl.dom.style.left = box.x + "px";
38426             this.collapsedEl.dom.style.top = box.y + "px";
38427             this.collapsedEl.setSize(box.width, box.height);
38428         }
38429         if(this.tabs){
38430             this.tabs.autoSizeTabs();
38431         }
38432     },
38433
38434     updateBody : function(w, h)
38435     {
38436         if(w !== null){
38437             this.el.setWidth(w);
38438             w -= this.el.getBorderWidth("rl");
38439             if(this.config.adjustments){
38440                 w += this.config.adjustments[0];
38441             }
38442         }
38443         if(h !== null && h > 0){
38444             this.el.setHeight(h);
38445             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38446             h -= this.el.getBorderWidth("tb");
38447             if(this.config.adjustments){
38448                 h += this.config.adjustments[1];
38449             }
38450             this.bodyEl.setHeight(h);
38451             if(this.tabs){
38452                 h = this.tabs.syncHeight(h);
38453             }
38454         }
38455         if(this.panelSize){
38456             w = w !== null ? w : this.panelSize.width;
38457             h = h !== null ? h : this.panelSize.height;
38458         }
38459         if(this.activePanel){
38460             var el = this.activePanel.getEl();
38461             w = w !== null ? w : el.getWidth();
38462             h = h !== null ? h : el.getHeight();
38463             this.panelSize = {width: w, height: h};
38464             this.activePanel.setSize(w, h);
38465         }
38466         if(Roo.isIE && this.tabs){
38467             this.tabs.el.repaint();
38468         }
38469     },
38470
38471     /**
38472      * Returns the container element for this region.
38473      * @return {Roo.Element}
38474      */
38475     getEl : function(){
38476         return this.el;
38477     },
38478
38479     /**
38480      * Hides this region.
38481      */
38482     hide : function(){
38483         //if(!this.collapsed){
38484             this.el.dom.style.left = "-2000px";
38485             this.el.hide();
38486         //}else{
38487          //   this.collapsedEl.dom.style.left = "-2000px";
38488          //   this.collapsedEl.hide();
38489        // }
38490         this.visible = false;
38491         this.fireEvent("visibilitychange", this, false);
38492     },
38493
38494     /**
38495      * Shows this region if it was previously hidden.
38496      */
38497     show : function(){
38498         //if(!this.collapsed){
38499             this.el.show();
38500         //}else{
38501         //    this.collapsedEl.show();
38502        // }
38503         this.visible = true;
38504         this.fireEvent("visibilitychange", this, true);
38505     },
38506 /*
38507     closeClicked : function(){
38508         if(this.activePanel){
38509             this.remove(this.activePanel);
38510         }
38511     },
38512
38513     collapseClick : function(e){
38514         if(this.isSlid){
38515            e.stopPropagation();
38516            this.slideIn();
38517         }else{
38518            e.stopPropagation();
38519            this.slideOut();
38520         }
38521     },
38522 */
38523     /**
38524      * Collapses this region.
38525      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38526      */
38527     /*
38528     collapse : function(skipAnim, skipCheck = false){
38529         if(this.collapsed) {
38530             return;
38531         }
38532         
38533         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38534             
38535             this.collapsed = true;
38536             if(this.split){
38537                 this.split.el.hide();
38538             }
38539             if(this.config.animate && skipAnim !== true){
38540                 this.fireEvent("invalidated", this);
38541                 this.animateCollapse();
38542             }else{
38543                 this.el.setLocation(-20000,-20000);
38544                 this.el.hide();
38545                 this.collapsedEl.show();
38546                 this.fireEvent("collapsed", this);
38547                 this.fireEvent("invalidated", this);
38548             }
38549         }
38550         
38551     },
38552 */
38553     animateCollapse : function(){
38554         // overridden
38555     },
38556
38557     /**
38558      * Expands this region if it was previously collapsed.
38559      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38560      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38561      */
38562     /*
38563     expand : function(e, skipAnim){
38564         if(e) {
38565             e.stopPropagation();
38566         }
38567         if(!this.collapsed || this.el.hasActiveFx()) {
38568             return;
38569         }
38570         if(this.isSlid){
38571             this.afterSlideIn();
38572             skipAnim = true;
38573         }
38574         this.collapsed = false;
38575         if(this.config.animate && skipAnim !== true){
38576             this.animateExpand();
38577         }else{
38578             this.el.show();
38579             if(this.split){
38580                 this.split.el.show();
38581             }
38582             this.collapsedEl.setLocation(-2000,-2000);
38583             this.collapsedEl.hide();
38584             this.fireEvent("invalidated", this);
38585             this.fireEvent("expanded", this);
38586         }
38587     },
38588 */
38589     animateExpand : function(){
38590         // overridden
38591     },
38592
38593     initTabs : function()
38594     {
38595         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38596         
38597         var ts = new Roo.bootstrap.panel.Tabs({
38598             el: this.bodyEl.dom,
38599             region : this,
38600             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38601             disableTooltips: this.config.disableTabTips,
38602             toolbar : this.config.toolbar
38603         });
38604         
38605         if(this.config.hideTabs){
38606             ts.stripWrap.setDisplayed(false);
38607         }
38608         this.tabs = ts;
38609         ts.resizeTabs = this.config.resizeTabs === true;
38610         ts.minTabWidth = this.config.minTabWidth || 40;
38611         ts.maxTabWidth = this.config.maxTabWidth || 250;
38612         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38613         ts.monitorResize = false;
38614         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38615         ts.bodyEl.addClass('roo-layout-tabs-body');
38616         this.panels.each(this.initPanelAsTab, this);
38617     },
38618
38619     initPanelAsTab : function(panel){
38620         var ti = this.tabs.addTab(
38621             panel.getEl().id,
38622             panel.getTitle(),
38623             null,
38624             this.config.closeOnTab && panel.isClosable(),
38625             panel.tpl
38626         );
38627         if(panel.tabTip !== undefined){
38628             ti.setTooltip(panel.tabTip);
38629         }
38630         ti.on("activate", function(){
38631               this.setActivePanel(panel);
38632         }, this);
38633         
38634         if(this.config.closeOnTab){
38635             ti.on("beforeclose", function(t, e){
38636                 e.cancel = true;
38637                 this.remove(panel);
38638             }, this);
38639         }
38640         
38641         panel.tabItem = ti;
38642         
38643         return ti;
38644     },
38645
38646     updatePanelTitle : function(panel, title)
38647     {
38648         if(this.activePanel == panel){
38649             this.updateTitle(title);
38650         }
38651         if(this.tabs){
38652             var ti = this.tabs.getTab(panel.getEl().id);
38653             ti.setText(title);
38654             if(panel.tabTip !== undefined){
38655                 ti.setTooltip(panel.tabTip);
38656             }
38657         }
38658     },
38659
38660     updateTitle : function(title){
38661         if(this.titleTextEl && !this.config.title){
38662             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38663         }
38664     },
38665
38666     setActivePanel : function(panel)
38667     {
38668         panel = this.getPanel(panel);
38669         if(this.activePanel && this.activePanel != panel){
38670             if(this.activePanel.setActiveState(false) === false){
38671                 return;
38672             }
38673         }
38674         this.activePanel = panel;
38675         panel.setActiveState(true);
38676         if(this.panelSize){
38677             panel.setSize(this.panelSize.width, this.panelSize.height);
38678         }
38679         if(this.closeBtn){
38680             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38681         }
38682         this.updateTitle(panel.getTitle());
38683         if(this.tabs){
38684             this.fireEvent("invalidated", this);
38685         }
38686         this.fireEvent("panelactivated", this, panel);
38687     },
38688
38689     /**
38690      * Shows the specified panel.
38691      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38692      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38693      */
38694     showPanel : function(panel)
38695     {
38696         panel = this.getPanel(panel);
38697         if(panel){
38698             if(this.tabs){
38699                 var tab = this.tabs.getTab(panel.getEl().id);
38700                 if(tab.isHidden()){
38701                     this.tabs.unhideTab(tab.id);
38702                 }
38703                 tab.activate();
38704             }else{
38705                 this.setActivePanel(panel);
38706             }
38707         }
38708         return panel;
38709     },
38710
38711     /**
38712      * Get the active panel for this region.
38713      * @return {Roo.ContentPanel} The active panel or null
38714      */
38715     getActivePanel : function(){
38716         return this.activePanel;
38717     },
38718
38719     validateVisibility : function(){
38720         if(this.panels.getCount() < 1){
38721             this.updateTitle("&#160;");
38722             this.closeBtn.hide();
38723             this.hide();
38724         }else{
38725             if(!this.isVisible()){
38726                 this.show();
38727             }
38728         }
38729     },
38730
38731     /**
38732      * Adds the passed ContentPanel(s) to this region.
38733      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38734      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38735      */
38736     add : function(panel)
38737     {
38738         if(arguments.length > 1){
38739             for(var i = 0, len = arguments.length; i < len; i++) {
38740                 this.add(arguments[i]);
38741             }
38742             return null;
38743         }
38744         
38745         // if we have not been rendered yet, then we can not really do much of this..
38746         if (!this.bodyEl) {
38747             this.unrendered_panels.push(panel);
38748             return panel;
38749         }
38750         
38751         
38752         
38753         
38754         if(this.hasPanel(panel)){
38755             this.showPanel(panel);
38756             return panel;
38757         }
38758         panel.setRegion(this);
38759         this.panels.add(panel);
38760        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38761             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38762             // and hide them... ???
38763             this.bodyEl.dom.appendChild(panel.getEl().dom);
38764             if(panel.background !== true){
38765                 this.setActivePanel(panel);
38766             }
38767             this.fireEvent("paneladded", this, panel);
38768             return panel;
38769         }
38770         */
38771         if(!this.tabs){
38772             this.initTabs();
38773         }else{
38774             this.initPanelAsTab(panel);
38775         }
38776         
38777         
38778         if(panel.background !== true){
38779             this.tabs.activate(panel.getEl().id);
38780         }
38781         this.fireEvent("paneladded", this, panel);
38782         return panel;
38783     },
38784
38785     /**
38786      * Hides the tab for the specified panel.
38787      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38788      */
38789     hidePanel : function(panel){
38790         if(this.tabs && (panel = this.getPanel(panel))){
38791             this.tabs.hideTab(panel.getEl().id);
38792         }
38793     },
38794
38795     /**
38796      * Unhides the tab for a previously hidden panel.
38797      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38798      */
38799     unhidePanel : function(panel){
38800         if(this.tabs && (panel = this.getPanel(panel))){
38801             this.tabs.unhideTab(panel.getEl().id);
38802         }
38803     },
38804
38805     clearPanels : function(){
38806         while(this.panels.getCount() > 0){
38807              this.remove(this.panels.first());
38808         }
38809     },
38810
38811     /**
38812      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38813      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38814      * @param {Boolean} preservePanel Overrides the config preservePanel option
38815      * @return {Roo.ContentPanel} The panel that was removed
38816      */
38817     remove : function(panel, preservePanel)
38818     {
38819         panel = this.getPanel(panel);
38820         if(!panel){
38821             return null;
38822         }
38823         var e = {};
38824         this.fireEvent("beforeremove", this, panel, e);
38825         if(e.cancel === true){
38826             return null;
38827         }
38828         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38829         var panelId = panel.getId();
38830         this.panels.removeKey(panelId);
38831         if(preservePanel){
38832             document.body.appendChild(panel.getEl().dom);
38833         }
38834         if(this.tabs){
38835             this.tabs.removeTab(panel.getEl().id);
38836         }else if (!preservePanel){
38837             this.bodyEl.dom.removeChild(panel.getEl().dom);
38838         }
38839         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38840             var p = this.panels.first();
38841             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38842             tempEl.appendChild(p.getEl().dom);
38843             this.bodyEl.update("");
38844             this.bodyEl.dom.appendChild(p.getEl().dom);
38845             tempEl = null;
38846             this.updateTitle(p.getTitle());
38847             this.tabs = null;
38848             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38849             this.setActivePanel(p);
38850         }
38851         panel.setRegion(null);
38852         if(this.activePanel == panel){
38853             this.activePanel = null;
38854         }
38855         if(this.config.autoDestroy !== false && preservePanel !== true){
38856             try{panel.destroy();}catch(e){}
38857         }
38858         this.fireEvent("panelremoved", this, panel);
38859         return panel;
38860     },
38861
38862     /**
38863      * Returns the TabPanel component used by this region
38864      * @return {Roo.TabPanel}
38865      */
38866     getTabs : function(){
38867         return this.tabs;
38868     },
38869
38870     createTool : function(parentEl, className){
38871         var btn = Roo.DomHelper.append(parentEl, {
38872             tag: "div",
38873             cls: "x-layout-tools-button",
38874             children: [ {
38875                 tag: "div",
38876                 cls: "roo-layout-tools-button-inner " + className,
38877                 html: "&#160;"
38878             }]
38879         }, true);
38880         btn.addClassOnOver("roo-layout-tools-button-over");
38881         return btn;
38882     }
38883 });/*
38884  * Based on:
38885  * Ext JS Library 1.1.1
38886  * Copyright(c) 2006-2007, Ext JS, LLC.
38887  *
38888  * Originally Released Under LGPL - original licence link has changed is not relivant.
38889  *
38890  * Fork - LGPL
38891  * <script type="text/javascript">
38892  */
38893  
38894
38895
38896 /**
38897  * @class Roo.SplitLayoutRegion
38898  * @extends Roo.LayoutRegion
38899  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38900  */
38901 Roo.bootstrap.layout.Split = function(config){
38902     this.cursor = config.cursor;
38903     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38904 };
38905
38906 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38907 {
38908     splitTip : "Drag to resize.",
38909     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38910     useSplitTips : false,
38911
38912     applyConfig : function(config){
38913         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38914     },
38915     
38916     onRender : function(ctr,pos) {
38917         
38918         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38919         if(!this.config.split){
38920             return;
38921         }
38922         if(!this.split){
38923             
38924             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38925                             tag: "div",
38926                             id: this.el.id + "-split",
38927                             cls: "roo-layout-split roo-layout-split-"+this.position,
38928                             html: "&#160;"
38929             });
38930             /** The SplitBar for this region 
38931             * @type Roo.SplitBar */
38932             // does not exist yet...
38933             Roo.log([this.position, this.orientation]);
38934             
38935             this.split = new Roo.bootstrap.SplitBar({
38936                 dragElement : splitEl,
38937                 resizingElement: this.el,
38938                 orientation : this.orientation
38939             });
38940             
38941             this.split.on("moved", this.onSplitMove, this);
38942             this.split.useShim = this.config.useShim === true;
38943             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38944             if(this.useSplitTips){
38945                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38946             }
38947             //if(config.collapsible){
38948             //    this.split.el.on("dblclick", this.collapse,  this);
38949             //}
38950         }
38951         if(typeof this.config.minSize != "undefined"){
38952             this.split.minSize = this.config.minSize;
38953         }
38954         if(typeof this.config.maxSize != "undefined"){
38955             this.split.maxSize = this.config.maxSize;
38956         }
38957         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38958             this.hideSplitter();
38959         }
38960         
38961     },
38962
38963     getHMaxSize : function(){
38964          var cmax = this.config.maxSize || 10000;
38965          var center = this.mgr.getRegion("center");
38966          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38967     },
38968
38969     getVMaxSize : function(){
38970          var cmax = this.config.maxSize || 10000;
38971          var center = this.mgr.getRegion("center");
38972          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38973     },
38974
38975     onSplitMove : function(split, newSize){
38976         this.fireEvent("resized", this, newSize);
38977     },
38978     
38979     /** 
38980      * Returns the {@link Roo.SplitBar} for this region.
38981      * @return {Roo.SplitBar}
38982      */
38983     getSplitBar : function(){
38984         return this.split;
38985     },
38986     
38987     hide : function(){
38988         this.hideSplitter();
38989         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38990     },
38991
38992     hideSplitter : function(){
38993         if(this.split){
38994             this.split.el.setLocation(-2000,-2000);
38995             this.split.el.hide();
38996         }
38997     },
38998
38999     show : function(){
39000         if(this.split){
39001             this.split.el.show();
39002         }
39003         Roo.bootstrap.layout.Split.superclass.show.call(this);
39004     },
39005     
39006     beforeSlide: function(){
39007         if(Roo.isGecko){// firefox overflow auto bug workaround
39008             this.bodyEl.clip();
39009             if(this.tabs) {
39010                 this.tabs.bodyEl.clip();
39011             }
39012             if(this.activePanel){
39013                 this.activePanel.getEl().clip();
39014                 
39015                 if(this.activePanel.beforeSlide){
39016                     this.activePanel.beforeSlide();
39017                 }
39018             }
39019         }
39020     },
39021     
39022     afterSlide : function(){
39023         if(Roo.isGecko){// firefox overflow auto bug workaround
39024             this.bodyEl.unclip();
39025             if(this.tabs) {
39026                 this.tabs.bodyEl.unclip();
39027             }
39028             if(this.activePanel){
39029                 this.activePanel.getEl().unclip();
39030                 if(this.activePanel.afterSlide){
39031                     this.activePanel.afterSlide();
39032                 }
39033             }
39034         }
39035     },
39036
39037     initAutoHide : function(){
39038         if(this.autoHide !== false){
39039             if(!this.autoHideHd){
39040                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39041                 this.autoHideHd = {
39042                     "mouseout": function(e){
39043                         if(!e.within(this.el, true)){
39044                             st.delay(500);
39045                         }
39046                     },
39047                     "mouseover" : function(e){
39048                         st.cancel();
39049                     },
39050                     scope : this
39051                 };
39052             }
39053             this.el.on(this.autoHideHd);
39054         }
39055     },
39056
39057     clearAutoHide : function(){
39058         if(this.autoHide !== false){
39059             this.el.un("mouseout", this.autoHideHd.mouseout);
39060             this.el.un("mouseover", this.autoHideHd.mouseover);
39061         }
39062     },
39063
39064     clearMonitor : function(){
39065         Roo.get(document).un("click", this.slideInIf, this);
39066     },
39067
39068     // these names are backwards but not changed for compat
39069     slideOut : function(){
39070         if(this.isSlid || this.el.hasActiveFx()){
39071             return;
39072         }
39073         this.isSlid = true;
39074         if(this.collapseBtn){
39075             this.collapseBtn.hide();
39076         }
39077         this.closeBtnState = this.closeBtn.getStyle('display');
39078         this.closeBtn.hide();
39079         if(this.stickBtn){
39080             this.stickBtn.show();
39081         }
39082         this.el.show();
39083         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39084         this.beforeSlide();
39085         this.el.setStyle("z-index", 10001);
39086         this.el.slideIn(this.getSlideAnchor(), {
39087             callback: function(){
39088                 this.afterSlide();
39089                 this.initAutoHide();
39090                 Roo.get(document).on("click", this.slideInIf, this);
39091                 this.fireEvent("slideshow", this);
39092             },
39093             scope: this,
39094             block: true
39095         });
39096     },
39097
39098     afterSlideIn : function(){
39099         this.clearAutoHide();
39100         this.isSlid = false;
39101         this.clearMonitor();
39102         this.el.setStyle("z-index", "");
39103         if(this.collapseBtn){
39104             this.collapseBtn.show();
39105         }
39106         this.closeBtn.setStyle('display', this.closeBtnState);
39107         if(this.stickBtn){
39108             this.stickBtn.hide();
39109         }
39110         this.fireEvent("slidehide", this);
39111     },
39112
39113     slideIn : function(cb){
39114         if(!this.isSlid || this.el.hasActiveFx()){
39115             Roo.callback(cb);
39116             return;
39117         }
39118         this.isSlid = false;
39119         this.beforeSlide();
39120         this.el.slideOut(this.getSlideAnchor(), {
39121             callback: function(){
39122                 this.el.setLeftTop(-10000, -10000);
39123                 this.afterSlide();
39124                 this.afterSlideIn();
39125                 Roo.callback(cb);
39126             },
39127             scope: this,
39128             block: true
39129         });
39130     },
39131     
39132     slideInIf : function(e){
39133         if(!e.within(this.el)){
39134             this.slideIn();
39135         }
39136     },
39137
39138     animateCollapse : function(){
39139         this.beforeSlide();
39140         this.el.setStyle("z-index", 20000);
39141         var anchor = this.getSlideAnchor();
39142         this.el.slideOut(anchor, {
39143             callback : function(){
39144                 this.el.setStyle("z-index", "");
39145                 this.collapsedEl.slideIn(anchor, {duration:.3});
39146                 this.afterSlide();
39147                 this.el.setLocation(-10000,-10000);
39148                 this.el.hide();
39149                 this.fireEvent("collapsed", this);
39150             },
39151             scope: this,
39152             block: true
39153         });
39154     },
39155
39156     animateExpand : function(){
39157         this.beforeSlide();
39158         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39159         this.el.setStyle("z-index", 20000);
39160         this.collapsedEl.hide({
39161             duration:.1
39162         });
39163         this.el.slideIn(this.getSlideAnchor(), {
39164             callback : function(){
39165                 this.el.setStyle("z-index", "");
39166                 this.afterSlide();
39167                 if(this.split){
39168                     this.split.el.show();
39169                 }
39170                 this.fireEvent("invalidated", this);
39171                 this.fireEvent("expanded", this);
39172             },
39173             scope: this,
39174             block: true
39175         });
39176     },
39177
39178     anchors : {
39179         "west" : "left",
39180         "east" : "right",
39181         "north" : "top",
39182         "south" : "bottom"
39183     },
39184
39185     sanchors : {
39186         "west" : "l",
39187         "east" : "r",
39188         "north" : "t",
39189         "south" : "b"
39190     },
39191
39192     canchors : {
39193         "west" : "tl-tr",
39194         "east" : "tr-tl",
39195         "north" : "tl-bl",
39196         "south" : "bl-tl"
39197     },
39198
39199     getAnchor : function(){
39200         return this.anchors[this.position];
39201     },
39202
39203     getCollapseAnchor : function(){
39204         return this.canchors[this.position];
39205     },
39206
39207     getSlideAnchor : function(){
39208         return this.sanchors[this.position];
39209     },
39210
39211     getAlignAdj : function(){
39212         var cm = this.cmargins;
39213         switch(this.position){
39214             case "west":
39215                 return [0, 0];
39216             break;
39217             case "east":
39218                 return [0, 0];
39219             break;
39220             case "north":
39221                 return [0, 0];
39222             break;
39223             case "south":
39224                 return [0, 0];
39225             break;
39226         }
39227     },
39228
39229     getExpandAdj : function(){
39230         var c = this.collapsedEl, cm = this.cmargins;
39231         switch(this.position){
39232             case "west":
39233                 return [-(cm.right+c.getWidth()+cm.left), 0];
39234             break;
39235             case "east":
39236                 return [cm.right+c.getWidth()+cm.left, 0];
39237             break;
39238             case "north":
39239                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39240             break;
39241             case "south":
39242                 return [0, cm.top+cm.bottom+c.getHeight()];
39243             break;
39244         }
39245     }
39246 });/*
39247  * Based on:
39248  * Ext JS Library 1.1.1
39249  * Copyright(c) 2006-2007, Ext JS, LLC.
39250  *
39251  * Originally Released Under LGPL - original licence link has changed is not relivant.
39252  *
39253  * Fork - LGPL
39254  * <script type="text/javascript">
39255  */
39256 /*
39257  * These classes are private internal classes
39258  */
39259 Roo.bootstrap.layout.Center = function(config){
39260     config.region = "center";
39261     Roo.bootstrap.layout.Region.call(this, config);
39262     this.visible = true;
39263     this.minWidth = config.minWidth || 20;
39264     this.minHeight = config.minHeight || 20;
39265 };
39266
39267 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39268     hide : function(){
39269         // center panel can't be hidden
39270     },
39271     
39272     show : function(){
39273         // center panel can't be hidden
39274     },
39275     
39276     getMinWidth: function(){
39277         return this.minWidth;
39278     },
39279     
39280     getMinHeight: function(){
39281         return this.minHeight;
39282     }
39283 });
39284
39285
39286
39287
39288  
39289
39290
39291
39292
39293
39294
39295 Roo.bootstrap.layout.North = function(config)
39296 {
39297     config.region = 'north';
39298     config.cursor = 'n-resize';
39299     
39300     Roo.bootstrap.layout.Split.call(this, config);
39301     
39302     
39303     if(this.split){
39304         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39305         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39306         this.split.el.addClass("roo-layout-split-v");
39307     }
39308     //var size = config.initialSize || config.height;
39309     //if(this.el && typeof size != "undefined"){
39310     //    this.el.setHeight(size);
39311     //}
39312 };
39313 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39314 {
39315     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39316      
39317      
39318     onRender : function(ctr, pos)
39319     {
39320         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39321         var size = this.config.initialSize || this.config.height;
39322         if(this.el && typeof size != "undefined"){
39323             this.el.setHeight(size);
39324         }
39325     
39326     },
39327     
39328     getBox : function(){
39329         if(this.collapsed){
39330             return this.collapsedEl.getBox();
39331         }
39332         var box = this.el.getBox();
39333         if(this.split){
39334             box.height += this.split.el.getHeight();
39335         }
39336         return box;
39337     },
39338     
39339     updateBox : function(box){
39340         if(this.split && !this.collapsed){
39341             box.height -= this.split.el.getHeight();
39342             this.split.el.setLeft(box.x);
39343             this.split.el.setTop(box.y+box.height);
39344             this.split.el.setWidth(box.width);
39345         }
39346         if(this.collapsed){
39347             this.updateBody(box.width, null);
39348         }
39349         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39350     }
39351 });
39352
39353
39354
39355
39356
39357 Roo.bootstrap.layout.South = function(config){
39358     config.region = 'south';
39359     config.cursor = 's-resize';
39360     Roo.bootstrap.layout.Split.call(this, config);
39361     if(this.split){
39362         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39363         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39364         this.split.el.addClass("roo-layout-split-v");
39365     }
39366     
39367 };
39368
39369 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39370     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39371     
39372     onRender : function(ctr, pos)
39373     {
39374         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39375         var size = this.config.initialSize || this.config.height;
39376         if(this.el && typeof size != "undefined"){
39377             this.el.setHeight(size);
39378         }
39379     
39380     },
39381     
39382     getBox : function(){
39383         if(this.collapsed){
39384             return this.collapsedEl.getBox();
39385         }
39386         var box = this.el.getBox();
39387         if(this.split){
39388             var sh = this.split.el.getHeight();
39389             box.height += sh;
39390             box.y -= sh;
39391         }
39392         return box;
39393     },
39394     
39395     updateBox : function(box){
39396         if(this.split && !this.collapsed){
39397             var sh = this.split.el.getHeight();
39398             box.height -= sh;
39399             box.y += sh;
39400             this.split.el.setLeft(box.x);
39401             this.split.el.setTop(box.y-sh);
39402             this.split.el.setWidth(box.width);
39403         }
39404         if(this.collapsed){
39405             this.updateBody(box.width, null);
39406         }
39407         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39408     }
39409 });
39410
39411 Roo.bootstrap.layout.East = function(config){
39412     config.region = "east";
39413     config.cursor = "e-resize";
39414     Roo.bootstrap.layout.Split.call(this, config);
39415     if(this.split){
39416         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39417         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39418         this.split.el.addClass("roo-layout-split-h");
39419     }
39420     
39421 };
39422 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39423     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39424     
39425     onRender : function(ctr, pos)
39426     {
39427         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39428         var size = this.config.initialSize || this.config.width;
39429         if(this.el && typeof size != "undefined"){
39430             this.el.setWidth(size);
39431         }
39432     
39433     },
39434     
39435     getBox : function(){
39436         if(this.collapsed){
39437             return this.collapsedEl.getBox();
39438         }
39439         var box = this.el.getBox();
39440         if(this.split){
39441             var sw = this.split.el.getWidth();
39442             box.width += sw;
39443             box.x -= sw;
39444         }
39445         return box;
39446     },
39447
39448     updateBox : function(box){
39449         if(this.split && !this.collapsed){
39450             var sw = this.split.el.getWidth();
39451             box.width -= sw;
39452             this.split.el.setLeft(box.x);
39453             this.split.el.setTop(box.y);
39454             this.split.el.setHeight(box.height);
39455             box.x += sw;
39456         }
39457         if(this.collapsed){
39458             this.updateBody(null, box.height);
39459         }
39460         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39461     }
39462 });
39463
39464 Roo.bootstrap.layout.West = function(config){
39465     config.region = "west";
39466     config.cursor = "w-resize";
39467     
39468     Roo.bootstrap.layout.Split.call(this, config);
39469     if(this.split){
39470         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39471         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39472         this.split.el.addClass("roo-layout-split-h");
39473     }
39474     
39475 };
39476 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39477     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39478     
39479     onRender: function(ctr, pos)
39480     {
39481         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39482         var size = this.config.initialSize || this.config.width;
39483         if(typeof size != "undefined"){
39484             this.el.setWidth(size);
39485         }
39486     },
39487     
39488     getBox : function(){
39489         if(this.collapsed){
39490             return this.collapsedEl.getBox();
39491         }
39492         var box = this.el.getBox();
39493         if (box.width == 0) {
39494             box.width = this.config.width; // kludge?
39495         }
39496         if(this.split){
39497             box.width += this.split.el.getWidth();
39498         }
39499         return box;
39500     },
39501     
39502     updateBox : function(box){
39503         if(this.split && !this.collapsed){
39504             var sw = this.split.el.getWidth();
39505             box.width -= sw;
39506             this.split.el.setLeft(box.x+box.width);
39507             this.split.el.setTop(box.y);
39508             this.split.el.setHeight(box.height);
39509         }
39510         if(this.collapsed){
39511             this.updateBody(null, box.height);
39512         }
39513         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39514     }
39515 });Roo.namespace("Roo.bootstrap.panel");/*
39516  * Based on:
39517  * Ext JS Library 1.1.1
39518  * Copyright(c) 2006-2007, Ext JS, LLC.
39519  *
39520  * Originally Released Under LGPL - original licence link has changed is not relivant.
39521  *
39522  * Fork - LGPL
39523  * <script type="text/javascript">
39524  */
39525 /**
39526  * @class Roo.ContentPanel
39527  * @extends Roo.util.Observable
39528  * A basic ContentPanel element.
39529  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39530  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39531  * @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
39532  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39533  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39534  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39535  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39536  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39537  * @cfg {String} title          The title for this panel
39538  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39539  * @cfg {String} url            Calls {@link #setUrl} with this value
39540  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39541  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39542  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39543  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39544  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39545  * @cfg {Boolean} badges render the badges
39546  * @cfg {String} cls  extra classes to use  
39547  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39548
39549  * @constructor
39550  * Create a new ContentPanel.
39551  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39552  * @param {String/Object} config A string to set only the title or a config object
39553  * @param {String} content (optional) Set the HTML content for this panel
39554  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39555  */
39556 Roo.bootstrap.panel.Content = function( config){
39557     
39558     this.tpl = config.tpl || false;
39559     
39560     var el = config.el;
39561     var content = config.content;
39562
39563     if(config.autoCreate){ // xtype is available if this is called from factory
39564         el = Roo.id();
39565     }
39566     this.el = Roo.get(el);
39567     if(!this.el && config && config.autoCreate){
39568         if(typeof config.autoCreate == "object"){
39569             if(!config.autoCreate.id){
39570                 config.autoCreate.id = config.id||el;
39571             }
39572             this.el = Roo.DomHelper.append(document.body,
39573                         config.autoCreate, true);
39574         }else{
39575             var elcfg =  {
39576                 tag: "div",
39577                 cls: (config.cls || '') +
39578                     (config.background ? ' bg-' + config.background : '') +
39579                     " roo-layout-inactive-content",
39580                 id: config.id||el
39581             };
39582             if (config.iframe) {
39583                 elcfg.cn = [
39584                     {
39585                         tag : 'iframe',
39586                         style : 'border: 0px',
39587                         src : 'about:blank'
39588                     }
39589                 ];
39590             }
39591               
39592             if (config.html) {
39593                 elcfg.html = config.html;
39594                 
39595             }
39596                         
39597             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39598             if (config.iframe) {
39599                 this.iframeEl = this.el.select('iframe',true).first();
39600             }
39601             
39602         }
39603     } 
39604     this.closable = false;
39605     this.loaded = false;
39606     this.active = false;
39607    
39608       
39609     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39610         
39611         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39612         
39613         this.wrapEl = this.el; //this.el.wrap();
39614         var ti = [];
39615         if (config.toolbar.items) {
39616             ti = config.toolbar.items ;
39617             delete config.toolbar.items ;
39618         }
39619         
39620         var nitems = [];
39621         this.toolbar.render(this.wrapEl, 'before');
39622         for(var i =0;i < ti.length;i++) {
39623           //  Roo.log(['add child', items[i]]);
39624             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39625         }
39626         this.toolbar.items = nitems;
39627         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39628         delete config.toolbar;
39629         
39630     }
39631     /*
39632     // xtype created footer. - not sure if will work as we normally have to render first..
39633     if (this.footer && !this.footer.el && this.footer.xtype) {
39634         if (!this.wrapEl) {
39635             this.wrapEl = this.el.wrap();
39636         }
39637     
39638         this.footer.container = this.wrapEl.createChild();
39639          
39640         this.footer = Roo.factory(this.footer, Roo);
39641         
39642     }
39643     */
39644     
39645      if(typeof config == "string"){
39646         this.title = config;
39647     }else{
39648         Roo.apply(this, config);
39649     }
39650     
39651     if(this.resizeEl){
39652         this.resizeEl = Roo.get(this.resizeEl, true);
39653     }else{
39654         this.resizeEl = this.el;
39655     }
39656     // handle view.xtype
39657     
39658  
39659     
39660     
39661     this.addEvents({
39662         /**
39663          * @event activate
39664          * Fires when this panel is activated. 
39665          * @param {Roo.ContentPanel} this
39666          */
39667         "activate" : true,
39668         /**
39669          * @event deactivate
39670          * Fires when this panel is activated. 
39671          * @param {Roo.ContentPanel} this
39672          */
39673         "deactivate" : true,
39674
39675         /**
39676          * @event resize
39677          * Fires when this panel is resized if fitToFrame is true.
39678          * @param {Roo.ContentPanel} this
39679          * @param {Number} width The width after any component adjustments
39680          * @param {Number} height The height after any component adjustments
39681          */
39682         "resize" : true,
39683         
39684          /**
39685          * @event render
39686          * Fires when this tab is created
39687          * @param {Roo.ContentPanel} this
39688          */
39689         "render" : true
39690         
39691         
39692         
39693     });
39694     
39695
39696     
39697     
39698     if(this.autoScroll && !this.iframe){
39699         this.resizeEl.setStyle("overflow", "auto");
39700     } else {
39701         // fix randome scrolling
39702         //this.el.on('scroll', function() {
39703         //    Roo.log('fix random scolling');
39704         //    this.scrollTo('top',0); 
39705         //});
39706     }
39707     content = content || this.content;
39708     if(content){
39709         this.setContent(content);
39710     }
39711     if(config && config.url){
39712         this.setUrl(this.url, this.params, this.loadOnce);
39713     }
39714     
39715     
39716     
39717     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39718     
39719     if (this.view && typeof(this.view.xtype) != 'undefined') {
39720         this.view.el = this.el.appendChild(document.createElement("div"));
39721         this.view = Roo.factory(this.view); 
39722         this.view.render  &&  this.view.render(false, '');  
39723     }
39724     
39725     
39726     this.fireEvent('render', this);
39727 };
39728
39729 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39730     
39731     cls : '',
39732     background : '',
39733     
39734     tabTip : '',
39735     
39736     iframe : false,
39737     iframeEl : false,
39738     
39739     setRegion : function(region){
39740         this.region = region;
39741         this.setActiveClass(region && !this.background);
39742     },
39743     
39744     
39745     setActiveClass: function(state)
39746     {
39747         if(state){
39748            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39749            this.el.setStyle('position','relative');
39750         }else{
39751            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39752            this.el.setStyle('position', 'absolute');
39753         } 
39754     },
39755     
39756     /**
39757      * Returns the toolbar for this Panel if one was configured. 
39758      * @return {Roo.Toolbar} 
39759      */
39760     getToolbar : function(){
39761         return this.toolbar;
39762     },
39763     
39764     setActiveState : function(active)
39765     {
39766         this.active = active;
39767         this.setActiveClass(active);
39768         if(!active){
39769             if(this.fireEvent("deactivate", this) === false){
39770                 return false;
39771             }
39772             return true;
39773         }
39774         this.fireEvent("activate", this);
39775         return true;
39776     },
39777     /**
39778      * Updates this panel's element (not for iframe)
39779      * @param {String} content The new content
39780      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39781     */
39782     setContent : function(content, loadScripts){
39783         if (this.iframe) {
39784             return;
39785         }
39786         
39787         this.el.update(content, loadScripts);
39788     },
39789
39790     ignoreResize : function(w, h){
39791         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39792             return true;
39793         }else{
39794             this.lastSize = {width: w, height: h};
39795             return false;
39796         }
39797     },
39798     /**
39799      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39800      * @return {Roo.UpdateManager} The UpdateManager
39801      */
39802     getUpdateManager : function(){
39803         if (this.iframe) {
39804             return false;
39805         }
39806         return this.el.getUpdateManager();
39807     },
39808      /**
39809      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39810      * Does not work with IFRAME contents
39811      * @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:
39812 <pre><code>
39813 panel.load({
39814     url: "your-url.php",
39815     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39816     callback: yourFunction,
39817     scope: yourObject, //(optional scope)
39818     discardUrl: false,
39819     nocache: false,
39820     text: "Loading...",
39821     timeout: 30,
39822     scripts: false
39823 });
39824 </code></pre>
39825      
39826      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39827      * 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.
39828      * @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}
39829      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39830      * @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.
39831      * @return {Roo.ContentPanel} this
39832      */
39833     load : function(){
39834         
39835         if (this.iframe) {
39836             return this;
39837         }
39838         
39839         var um = this.el.getUpdateManager();
39840         um.update.apply(um, arguments);
39841         return this;
39842     },
39843
39844
39845     /**
39846      * 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.
39847      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39848      * @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)
39849      * @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)
39850      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39851      */
39852     setUrl : function(url, params, loadOnce){
39853         if (this.iframe) {
39854             this.iframeEl.dom.src = url;
39855             return false;
39856         }
39857         
39858         if(this.refreshDelegate){
39859             this.removeListener("activate", this.refreshDelegate);
39860         }
39861         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39862         this.on("activate", this.refreshDelegate);
39863         return this.el.getUpdateManager();
39864     },
39865     
39866     _handleRefresh : function(url, params, loadOnce){
39867         if(!loadOnce || !this.loaded){
39868             var updater = this.el.getUpdateManager();
39869             updater.update(url, params, this._setLoaded.createDelegate(this));
39870         }
39871     },
39872     
39873     _setLoaded : function(){
39874         this.loaded = true;
39875     }, 
39876     
39877     /**
39878      * Returns this panel's id
39879      * @return {String} 
39880      */
39881     getId : function(){
39882         return this.el.id;
39883     },
39884     
39885     /** 
39886      * Returns this panel's element - used by regiosn to add.
39887      * @return {Roo.Element} 
39888      */
39889     getEl : function(){
39890         return this.wrapEl || this.el;
39891     },
39892     
39893    
39894     
39895     adjustForComponents : function(width, height)
39896     {
39897         //Roo.log('adjustForComponents ');
39898         if(this.resizeEl != this.el){
39899             width -= this.el.getFrameWidth('lr');
39900             height -= this.el.getFrameWidth('tb');
39901         }
39902         if(this.toolbar){
39903             var te = this.toolbar.getEl();
39904             te.setWidth(width);
39905             height -= te.getHeight();
39906         }
39907         if(this.footer){
39908             var te = this.footer.getEl();
39909             te.setWidth(width);
39910             height -= te.getHeight();
39911         }
39912         
39913         
39914         if(this.adjustments){
39915             width += this.adjustments[0];
39916             height += this.adjustments[1];
39917         }
39918         return {"width": width, "height": height};
39919     },
39920     
39921     setSize : function(width, height){
39922         if(this.fitToFrame && !this.ignoreResize(width, height)){
39923             if(this.fitContainer && this.resizeEl != this.el){
39924                 this.el.setSize(width, height);
39925             }
39926             var size = this.adjustForComponents(width, height);
39927             if (this.iframe) {
39928                 this.iframeEl.setSize(width,height);
39929             }
39930             
39931             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39932             this.fireEvent('resize', this, size.width, size.height);
39933             
39934             
39935         }
39936     },
39937     
39938     /**
39939      * Returns this panel's title
39940      * @return {String} 
39941      */
39942     getTitle : function(){
39943         
39944         if (typeof(this.title) != 'object') {
39945             return this.title;
39946         }
39947         
39948         var t = '';
39949         for (var k in this.title) {
39950             if (!this.title.hasOwnProperty(k)) {
39951                 continue;
39952             }
39953             
39954             if (k.indexOf('-') >= 0) {
39955                 var s = k.split('-');
39956                 for (var i = 0; i<s.length; i++) {
39957                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39958                 }
39959             } else {
39960                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39961             }
39962         }
39963         return t;
39964     },
39965     
39966     /**
39967      * Set this panel's title
39968      * @param {String} title
39969      */
39970     setTitle : function(title){
39971         this.title = title;
39972         if(this.region){
39973             this.region.updatePanelTitle(this, title);
39974         }
39975     },
39976     
39977     /**
39978      * Returns true is this panel was configured to be closable
39979      * @return {Boolean} 
39980      */
39981     isClosable : function(){
39982         return this.closable;
39983     },
39984     
39985     beforeSlide : function(){
39986         this.el.clip();
39987         this.resizeEl.clip();
39988     },
39989     
39990     afterSlide : function(){
39991         this.el.unclip();
39992         this.resizeEl.unclip();
39993     },
39994     
39995     /**
39996      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39997      *   Will fail silently if the {@link #setUrl} method has not been called.
39998      *   This does not activate the panel, just updates its content.
39999      */
40000     refresh : function(){
40001         if(this.refreshDelegate){
40002            this.loaded = false;
40003            this.refreshDelegate();
40004         }
40005     },
40006     
40007     /**
40008      * Destroys this panel
40009      */
40010     destroy : function(){
40011         this.el.removeAllListeners();
40012         var tempEl = document.createElement("span");
40013         tempEl.appendChild(this.el.dom);
40014         tempEl.innerHTML = "";
40015         this.el.remove();
40016         this.el = null;
40017     },
40018     
40019     /**
40020      * form - if the content panel contains a form - this is a reference to it.
40021      * @type {Roo.form.Form}
40022      */
40023     form : false,
40024     /**
40025      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40026      *    This contains a reference to it.
40027      * @type {Roo.View}
40028      */
40029     view : false,
40030     
40031       /**
40032      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40033      * <pre><code>
40034
40035 layout.addxtype({
40036        xtype : 'Form',
40037        items: [ .... ]
40038    }
40039 );
40040
40041 </code></pre>
40042      * @param {Object} cfg Xtype definition of item to add.
40043      */
40044     
40045     
40046     getChildContainer: function () {
40047         return this.getEl();
40048     }
40049     
40050     
40051     /*
40052         var  ret = new Roo.factory(cfg);
40053         return ret;
40054         
40055         
40056         // add form..
40057         if (cfg.xtype.match(/^Form$/)) {
40058             
40059             var el;
40060             //if (this.footer) {
40061             //    el = this.footer.container.insertSibling(false, 'before');
40062             //} else {
40063                 el = this.el.createChild();
40064             //}
40065
40066             this.form = new  Roo.form.Form(cfg);
40067             
40068             
40069             if ( this.form.allItems.length) {
40070                 this.form.render(el.dom);
40071             }
40072             return this.form;
40073         }
40074         // should only have one of theses..
40075         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40076             // views.. should not be just added - used named prop 'view''
40077             
40078             cfg.el = this.el.appendChild(document.createElement("div"));
40079             // factory?
40080             
40081             var ret = new Roo.factory(cfg);
40082              
40083              ret.render && ret.render(false, ''); // render blank..
40084             this.view = ret;
40085             return ret;
40086         }
40087         return false;
40088     }
40089     \*/
40090 });
40091  
40092 /**
40093  * @class Roo.bootstrap.panel.Grid
40094  * @extends Roo.bootstrap.panel.Content
40095  * @constructor
40096  * Create a new GridPanel.
40097  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40098  * @param {Object} config A the config object
40099   
40100  */
40101
40102
40103
40104 Roo.bootstrap.panel.Grid = function(config)
40105 {
40106     
40107       
40108     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40109         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40110
40111     config.el = this.wrapper;
40112     //this.el = this.wrapper;
40113     
40114       if (config.container) {
40115         // ctor'ed from a Border/panel.grid
40116         
40117         
40118         this.wrapper.setStyle("overflow", "hidden");
40119         this.wrapper.addClass('roo-grid-container');
40120
40121     }
40122     
40123     
40124     if(config.toolbar){
40125         var tool_el = this.wrapper.createChild();    
40126         this.toolbar = Roo.factory(config.toolbar);
40127         var ti = [];
40128         if (config.toolbar.items) {
40129             ti = config.toolbar.items ;
40130             delete config.toolbar.items ;
40131         }
40132         
40133         var nitems = [];
40134         this.toolbar.render(tool_el);
40135         for(var i =0;i < ti.length;i++) {
40136           //  Roo.log(['add child', items[i]]);
40137             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40138         }
40139         this.toolbar.items = nitems;
40140         
40141         delete config.toolbar;
40142     }
40143     
40144     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40145     config.grid.scrollBody = true;;
40146     config.grid.monitorWindowResize = false; // turn off autosizing
40147     config.grid.autoHeight = false;
40148     config.grid.autoWidth = false;
40149     
40150     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40151     
40152     if (config.background) {
40153         // render grid on panel activation (if panel background)
40154         this.on('activate', function(gp) {
40155             if (!gp.grid.rendered) {
40156                 gp.grid.render(this.wrapper);
40157                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40158             }
40159         });
40160             
40161     } else {
40162         this.grid.render(this.wrapper);
40163         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40164
40165     }
40166     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40167     // ??? needed ??? config.el = this.wrapper;
40168     
40169     
40170     
40171   
40172     // xtype created footer. - not sure if will work as we normally have to render first..
40173     if (this.footer && !this.footer.el && this.footer.xtype) {
40174         
40175         var ctr = this.grid.getView().getFooterPanel(true);
40176         this.footer.dataSource = this.grid.dataSource;
40177         this.footer = Roo.factory(this.footer, Roo);
40178         this.footer.render(ctr);
40179         
40180     }
40181     
40182     
40183     
40184     
40185      
40186 };
40187
40188 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40189     getId : function(){
40190         return this.grid.id;
40191     },
40192     
40193     /**
40194      * Returns the grid for this panel
40195      * @return {Roo.bootstrap.Table} 
40196      */
40197     getGrid : function(){
40198         return this.grid;    
40199     },
40200     
40201     setSize : function(width, height){
40202         if(!this.ignoreResize(width, height)){
40203             var grid = this.grid;
40204             var size = this.adjustForComponents(width, height);
40205             // tfoot is not a footer?
40206           
40207             
40208             var gridel = grid.getGridEl();
40209             gridel.setSize(size.width, size.height);
40210             
40211             var tbd = grid.getGridEl().select('tbody', true).first();
40212             var thd = grid.getGridEl().select('thead',true).first();
40213             var tbf= grid.getGridEl().select('tfoot', true).first();
40214
40215             if (tbf) {
40216                 size.height -= tbf.getHeight();
40217             }
40218             if (thd) {
40219                 size.height -= thd.getHeight();
40220             }
40221             
40222             tbd.setSize(size.width, size.height );
40223             // this is for the account management tab -seems to work there.
40224             var thd = grid.getGridEl().select('thead',true).first();
40225             //if (tbd) {
40226             //    tbd.setSize(size.width, size.height - thd.getHeight());
40227             //}
40228              
40229             grid.autoSize();
40230         }
40231     },
40232      
40233     
40234     
40235     beforeSlide : function(){
40236         this.grid.getView().scroller.clip();
40237     },
40238     
40239     afterSlide : function(){
40240         this.grid.getView().scroller.unclip();
40241     },
40242     
40243     destroy : function(){
40244         this.grid.destroy();
40245         delete this.grid;
40246         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40247     }
40248 });
40249
40250 /**
40251  * @class Roo.bootstrap.panel.Nest
40252  * @extends Roo.bootstrap.panel.Content
40253  * @constructor
40254  * Create a new Panel, that can contain a layout.Border.
40255  * 
40256  * 
40257  * @param {Roo.BorderLayout} layout The layout for this panel
40258  * @param {String/Object} config A string to set only the title or a config object
40259  */
40260 Roo.bootstrap.panel.Nest = function(config)
40261 {
40262     // construct with only one argument..
40263     /* FIXME - implement nicer consturctors
40264     if (layout.layout) {
40265         config = layout;
40266         layout = config.layout;
40267         delete config.layout;
40268     }
40269     if (layout.xtype && !layout.getEl) {
40270         // then layout needs constructing..
40271         layout = Roo.factory(layout, Roo);
40272     }
40273     */
40274     
40275     config.el =  config.layout.getEl();
40276     
40277     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40278     
40279     config.layout.monitorWindowResize = false; // turn off autosizing
40280     this.layout = config.layout;
40281     this.layout.getEl().addClass("roo-layout-nested-layout");
40282     this.layout.parent = this;
40283     
40284     
40285     
40286     
40287 };
40288
40289 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40290
40291     setSize : function(width, height){
40292         if(!this.ignoreResize(width, height)){
40293             var size = this.adjustForComponents(width, height);
40294             var el = this.layout.getEl();
40295             if (size.height < 1) {
40296                 el.setWidth(size.width);   
40297             } else {
40298                 el.setSize(size.width, size.height);
40299             }
40300             var touch = el.dom.offsetWidth;
40301             this.layout.layout();
40302             // ie requires a double layout on the first pass
40303             if(Roo.isIE && !this.initialized){
40304                 this.initialized = true;
40305                 this.layout.layout();
40306             }
40307         }
40308     },
40309     
40310     // activate all subpanels if not currently active..
40311     
40312     setActiveState : function(active){
40313         this.active = active;
40314         this.setActiveClass(active);
40315         
40316         if(!active){
40317             this.fireEvent("deactivate", this);
40318             return;
40319         }
40320         
40321         this.fireEvent("activate", this);
40322         // not sure if this should happen before or after..
40323         if (!this.layout) {
40324             return; // should not happen..
40325         }
40326         var reg = false;
40327         for (var r in this.layout.regions) {
40328             reg = this.layout.getRegion(r);
40329             if (reg.getActivePanel()) {
40330                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40331                 reg.setActivePanel(reg.getActivePanel());
40332                 continue;
40333             }
40334             if (!reg.panels.length) {
40335                 continue;
40336             }
40337             reg.showPanel(reg.getPanel(0));
40338         }
40339         
40340         
40341         
40342         
40343     },
40344     
40345     /**
40346      * Returns the nested BorderLayout for this panel
40347      * @return {Roo.BorderLayout} 
40348      */
40349     getLayout : function(){
40350         return this.layout;
40351     },
40352     
40353      /**
40354      * Adds a xtype elements to the layout of the nested panel
40355      * <pre><code>
40356
40357 panel.addxtype({
40358        xtype : 'ContentPanel',
40359        region: 'west',
40360        items: [ .... ]
40361    }
40362 );
40363
40364 panel.addxtype({
40365         xtype : 'NestedLayoutPanel',
40366         region: 'west',
40367         layout: {
40368            center: { },
40369            west: { }   
40370         },
40371         items : [ ... list of content panels or nested layout panels.. ]
40372    }
40373 );
40374 </code></pre>
40375      * @param {Object} cfg Xtype definition of item to add.
40376      */
40377     addxtype : function(cfg) {
40378         return this.layout.addxtype(cfg);
40379     
40380     }
40381 });/*
40382  * Based on:
40383  * Ext JS Library 1.1.1
40384  * Copyright(c) 2006-2007, Ext JS, LLC.
40385  *
40386  * Originally Released Under LGPL - original licence link has changed is not relivant.
40387  *
40388  * Fork - LGPL
40389  * <script type="text/javascript">
40390  */
40391 /**
40392  * @class Roo.TabPanel
40393  * @extends Roo.util.Observable
40394  * A lightweight tab container.
40395  * <br><br>
40396  * Usage:
40397  * <pre><code>
40398 // basic tabs 1, built from existing content
40399 var tabs = new Roo.TabPanel("tabs1");
40400 tabs.addTab("script", "View Script");
40401 tabs.addTab("markup", "View Markup");
40402 tabs.activate("script");
40403
40404 // more advanced tabs, built from javascript
40405 var jtabs = new Roo.TabPanel("jtabs");
40406 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40407
40408 // set up the UpdateManager
40409 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40410 var updater = tab2.getUpdateManager();
40411 updater.setDefaultUrl("ajax1.htm");
40412 tab2.on('activate', updater.refresh, updater, true);
40413
40414 // Use setUrl for Ajax loading
40415 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40416 tab3.setUrl("ajax2.htm", null, true);
40417
40418 // Disabled tab
40419 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40420 tab4.disable();
40421
40422 jtabs.activate("jtabs-1");
40423  * </code></pre>
40424  * @constructor
40425  * Create a new TabPanel.
40426  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40427  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40428  */
40429 Roo.bootstrap.panel.Tabs = function(config){
40430     /**
40431     * The container element for this TabPanel.
40432     * @type Roo.Element
40433     */
40434     this.el = Roo.get(config.el);
40435     delete config.el;
40436     if(config){
40437         if(typeof config == "boolean"){
40438             this.tabPosition = config ? "bottom" : "top";
40439         }else{
40440             Roo.apply(this, config);
40441         }
40442     }
40443     
40444     if(this.tabPosition == "bottom"){
40445         // if tabs are at the bottom = create the body first.
40446         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40447         this.el.addClass("roo-tabs-bottom");
40448     }
40449     // next create the tabs holders
40450     
40451     if (this.tabPosition == "west"){
40452         
40453         var reg = this.region; // fake it..
40454         while (reg) {
40455             if (!reg.mgr.parent) {
40456                 break;
40457             }
40458             reg = reg.mgr.parent.region;
40459         }
40460         Roo.log("got nest?");
40461         Roo.log(reg);
40462         if (reg.mgr.getRegion('west')) {
40463             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40464             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40465             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40466             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40467             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40468         
40469             
40470         }
40471         
40472         
40473     } else {
40474      
40475         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40476         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40477         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40478         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40479     }
40480     
40481     
40482     if(Roo.isIE){
40483         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40484     }
40485     
40486     // finally - if tabs are at the top, then create the body last..
40487     if(this.tabPosition != "bottom"){
40488         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40489          * @type Roo.Element
40490          */
40491         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40492         this.el.addClass("roo-tabs-top");
40493     }
40494     this.items = [];
40495
40496     this.bodyEl.setStyle("position", "relative");
40497
40498     this.active = null;
40499     this.activateDelegate = this.activate.createDelegate(this);
40500
40501     this.addEvents({
40502         /**
40503          * @event tabchange
40504          * Fires when the active tab changes
40505          * @param {Roo.TabPanel} this
40506          * @param {Roo.TabPanelItem} activePanel The new active tab
40507          */
40508         "tabchange": true,
40509         /**
40510          * @event beforetabchange
40511          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40512          * @param {Roo.TabPanel} this
40513          * @param {Object} e Set cancel to true on this object to cancel the tab change
40514          * @param {Roo.TabPanelItem} tab The tab being changed to
40515          */
40516         "beforetabchange" : true
40517     });
40518
40519     Roo.EventManager.onWindowResize(this.onResize, this);
40520     this.cpad = this.el.getPadding("lr");
40521     this.hiddenCount = 0;
40522
40523
40524     // toolbar on the tabbar support...
40525     if (this.toolbar) {
40526         alert("no toolbar support yet");
40527         this.toolbar  = false;
40528         /*
40529         var tcfg = this.toolbar;
40530         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40531         this.toolbar = new Roo.Toolbar(tcfg);
40532         if (Roo.isSafari) {
40533             var tbl = tcfg.container.child('table', true);
40534             tbl.setAttribute('width', '100%');
40535         }
40536         */
40537         
40538     }
40539    
40540
40541
40542     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40543 };
40544
40545 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40546     /*
40547      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40548      */
40549     tabPosition : "top",
40550     /*
40551      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40552      */
40553     currentTabWidth : 0,
40554     /*
40555      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40556      */
40557     minTabWidth : 40,
40558     /*
40559      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40560      */
40561     maxTabWidth : 250,
40562     /*
40563      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40564      */
40565     preferredTabWidth : 175,
40566     /*
40567      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40568      */
40569     resizeTabs : false,
40570     /*
40571      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40572      */
40573     monitorResize : true,
40574     /*
40575      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40576      */
40577     toolbar : false,  // set by caller..
40578     
40579     region : false, /// set by caller
40580     
40581     disableTooltips : true, // not used yet...
40582
40583     /**
40584      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40585      * @param {String} id The id of the div to use <b>or create</b>
40586      * @param {String} text The text for the tab
40587      * @param {String} content (optional) Content to put in the TabPanelItem body
40588      * @param {Boolean} closable (optional) True to create a close icon on the tab
40589      * @return {Roo.TabPanelItem} The created TabPanelItem
40590      */
40591     addTab : function(id, text, content, closable, tpl)
40592     {
40593         var item = new Roo.bootstrap.panel.TabItem({
40594             panel: this,
40595             id : id,
40596             text : text,
40597             closable : closable,
40598             tpl : tpl
40599         });
40600         this.addTabItem(item);
40601         if(content){
40602             item.setContent(content);
40603         }
40604         return item;
40605     },
40606
40607     /**
40608      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40609      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40610      * @return {Roo.TabPanelItem}
40611      */
40612     getTab : function(id){
40613         return this.items[id];
40614     },
40615
40616     /**
40617      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40618      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40619      */
40620     hideTab : function(id){
40621         var t = this.items[id];
40622         if(!t.isHidden()){
40623            t.setHidden(true);
40624            this.hiddenCount++;
40625            this.autoSizeTabs();
40626         }
40627     },
40628
40629     /**
40630      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40631      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40632      */
40633     unhideTab : function(id){
40634         var t = this.items[id];
40635         if(t.isHidden()){
40636            t.setHidden(false);
40637            this.hiddenCount--;
40638            this.autoSizeTabs();
40639         }
40640     },
40641
40642     /**
40643      * Adds an existing {@link Roo.TabPanelItem}.
40644      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40645      */
40646     addTabItem : function(item)
40647     {
40648         this.items[item.id] = item;
40649         this.items.push(item);
40650         this.autoSizeTabs();
40651       //  if(this.resizeTabs){
40652     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40653   //         this.autoSizeTabs();
40654 //        }else{
40655 //            item.autoSize();
40656        // }
40657     },
40658
40659     /**
40660      * Removes a {@link Roo.TabPanelItem}.
40661      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40662      */
40663     removeTab : function(id){
40664         var items = this.items;
40665         var tab = items[id];
40666         if(!tab) { return; }
40667         var index = items.indexOf(tab);
40668         if(this.active == tab && items.length > 1){
40669             var newTab = this.getNextAvailable(index);
40670             if(newTab) {
40671                 newTab.activate();
40672             }
40673         }
40674         this.stripEl.dom.removeChild(tab.pnode.dom);
40675         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40676             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40677         }
40678         items.splice(index, 1);
40679         delete this.items[tab.id];
40680         tab.fireEvent("close", tab);
40681         tab.purgeListeners();
40682         this.autoSizeTabs();
40683     },
40684
40685     getNextAvailable : function(start){
40686         var items = this.items;
40687         var index = start;
40688         // look for a next tab that will slide over to
40689         // replace the one being removed
40690         while(index < items.length){
40691             var item = items[++index];
40692             if(item && !item.isHidden()){
40693                 return item;
40694             }
40695         }
40696         // if one isn't found select the previous tab (on the left)
40697         index = start;
40698         while(index >= 0){
40699             var item = items[--index];
40700             if(item && !item.isHidden()){
40701                 return item;
40702             }
40703         }
40704         return null;
40705     },
40706
40707     /**
40708      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40709      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40710      */
40711     disableTab : function(id){
40712         var tab = this.items[id];
40713         if(tab && this.active != tab){
40714             tab.disable();
40715         }
40716     },
40717
40718     /**
40719      * Enables a {@link Roo.TabPanelItem} that is disabled.
40720      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40721      */
40722     enableTab : function(id){
40723         var tab = this.items[id];
40724         tab.enable();
40725     },
40726
40727     /**
40728      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40729      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40730      * @return {Roo.TabPanelItem} The TabPanelItem.
40731      */
40732     activate : function(id)
40733     {
40734         //Roo.log('activite:'  + id);
40735         
40736         var tab = this.items[id];
40737         if(!tab){
40738             return null;
40739         }
40740         if(tab == this.active || tab.disabled){
40741             return tab;
40742         }
40743         var e = {};
40744         this.fireEvent("beforetabchange", this, e, tab);
40745         if(e.cancel !== true && !tab.disabled){
40746             if(this.active){
40747                 this.active.hide();
40748             }
40749             this.active = this.items[id];
40750             this.active.show();
40751             this.fireEvent("tabchange", this, this.active);
40752         }
40753         return tab;
40754     },
40755
40756     /**
40757      * Gets the active {@link Roo.TabPanelItem}.
40758      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40759      */
40760     getActiveTab : function(){
40761         return this.active;
40762     },
40763
40764     /**
40765      * Updates the tab body element to fit the height of the container element
40766      * for overflow scrolling
40767      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40768      */
40769     syncHeight : function(targetHeight){
40770         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40771         var bm = this.bodyEl.getMargins();
40772         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40773         this.bodyEl.setHeight(newHeight);
40774         return newHeight;
40775     },
40776
40777     onResize : function(){
40778         if(this.monitorResize){
40779             this.autoSizeTabs();
40780         }
40781     },
40782
40783     /**
40784      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40785      */
40786     beginUpdate : function(){
40787         this.updating = true;
40788     },
40789
40790     /**
40791      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40792      */
40793     endUpdate : function(){
40794         this.updating = false;
40795         this.autoSizeTabs();
40796     },
40797
40798     /**
40799      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40800      */
40801     autoSizeTabs : function()
40802     {
40803         var count = this.items.length;
40804         var vcount = count - this.hiddenCount;
40805         
40806         if (vcount < 2) {
40807             this.stripEl.hide();
40808         } else {
40809             this.stripEl.show();
40810         }
40811         
40812         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40813             return;
40814         }
40815         
40816         
40817         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40818         var availWidth = Math.floor(w / vcount);
40819         var b = this.stripBody;
40820         if(b.getWidth() > w){
40821             var tabs = this.items;
40822             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40823             if(availWidth < this.minTabWidth){
40824                 /*if(!this.sleft){    // incomplete scrolling code
40825                     this.createScrollButtons();
40826                 }
40827                 this.showScroll();
40828                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40829             }
40830         }else{
40831             if(this.currentTabWidth < this.preferredTabWidth){
40832                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40833             }
40834         }
40835     },
40836
40837     /**
40838      * Returns the number of tabs in this TabPanel.
40839      * @return {Number}
40840      */
40841      getCount : function(){
40842          return this.items.length;
40843      },
40844
40845     /**
40846      * Resizes all the tabs to the passed width
40847      * @param {Number} The new width
40848      */
40849     setTabWidth : function(width){
40850         this.currentTabWidth = width;
40851         for(var i = 0, len = this.items.length; i < len; i++) {
40852                 if(!this.items[i].isHidden()) {
40853                 this.items[i].setWidth(width);
40854             }
40855         }
40856     },
40857
40858     /**
40859      * Destroys this TabPanel
40860      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40861      */
40862     destroy : function(removeEl){
40863         Roo.EventManager.removeResizeListener(this.onResize, this);
40864         for(var i = 0, len = this.items.length; i < len; i++){
40865             this.items[i].purgeListeners();
40866         }
40867         if(removeEl === true){
40868             this.el.update("");
40869             this.el.remove();
40870         }
40871     },
40872     
40873     createStrip : function(container)
40874     {
40875         var strip = document.createElement("nav");
40876         strip.className = Roo.bootstrap.version == 4 ?
40877             "navbar-light bg-light" : 
40878             "navbar navbar-default"; //"x-tabs-wrap";
40879         container.appendChild(strip);
40880         return strip;
40881     },
40882     
40883     createStripList : function(strip)
40884     {
40885         // div wrapper for retard IE
40886         // returns the "tr" element.
40887         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40888         //'<div class="x-tabs-strip-wrap">'+
40889           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40890           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40891         return strip.firstChild; //.firstChild.firstChild.firstChild;
40892     },
40893     createBody : function(container)
40894     {
40895         var body = document.createElement("div");
40896         Roo.id(body, "tab-body");
40897         //Roo.fly(body).addClass("x-tabs-body");
40898         Roo.fly(body).addClass("tab-content");
40899         container.appendChild(body);
40900         return body;
40901     },
40902     createItemBody :function(bodyEl, id){
40903         var body = Roo.getDom(id);
40904         if(!body){
40905             body = document.createElement("div");
40906             body.id = id;
40907         }
40908         //Roo.fly(body).addClass("x-tabs-item-body");
40909         Roo.fly(body).addClass("tab-pane");
40910          bodyEl.insertBefore(body, bodyEl.firstChild);
40911         return body;
40912     },
40913     /** @private */
40914     createStripElements :  function(stripEl, text, closable, tpl)
40915     {
40916         var td = document.createElement("li"); // was td..
40917         td.className = 'nav-item';
40918         
40919         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40920         
40921         
40922         stripEl.appendChild(td);
40923         /*if(closable){
40924             td.className = "x-tabs-closable";
40925             if(!this.closeTpl){
40926                 this.closeTpl = new Roo.Template(
40927                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40928                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40929                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40930                 );
40931             }
40932             var el = this.closeTpl.overwrite(td, {"text": text});
40933             var close = el.getElementsByTagName("div")[0];
40934             var inner = el.getElementsByTagName("em")[0];
40935             return {"el": el, "close": close, "inner": inner};
40936         } else {
40937         */
40938         // not sure what this is..
40939 //            if(!this.tabTpl){
40940                 //this.tabTpl = new Roo.Template(
40941                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40942                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40943                 //);
40944 //                this.tabTpl = new Roo.Template(
40945 //                   '<a href="#">' +
40946 //                   '<span unselectable="on"' +
40947 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40948 //                            ' >{text}</span></a>'
40949 //                );
40950 //                
40951 //            }
40952
40953
40954             var template = tpl || this.tabTpl || false;
40955             
40956             if(!template){
40957                 template =  new Roo.Template(
40958                         Roo.bootstrap.version == 4 ? 
40959                             (
40960                                 '<a class="nav-link" href="#" unselectable="on"' +
40961                                      (this.disableTooltips ? '' : ' title="{text}"') +
40962                                      ' >{text}</a>'
40963                             ) : (
40964                                 '<a class="nav-link" href="#">' +
40965                                 '<span unselectable="on"' +
40966                                          (this.disableTooltips ? '' : ' title="{text}"') +
40967                                     ' >{text}</span></a>'
40968                             )
40969                 );
40970             }
40971             
40972             switch (typeof(template)) {
40973                 case 'object' :
40974                     break;
40975                 case 'string' :
40976                     template = new Roo.Template(template);
40977                     break;
40978                 default :
40979                     break;
40980             }
40981             
40982             var el = template.overwrite(td, {"text": text});
40983             
40984             var inner = el.getElementsByTagName("span")[0];
40985             
40986             return {"el": el, "inner": inner};
40987             
40988     }
40989         
40990     
40991 });
40992
40993 /**
40994  * @class Roo.TabPanelItem
40995  * @extends Roo.util.Observable
40996  * Represents an individual item (tab plus body) in a TabPanel.
40997  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40998  * @param {String} id The id of this TabPanelItem
40999  * @param {String} text The text for the tab of this TabPanelItem
41000  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41001  */
41002 Roo.bootstrap.panel.TabItem = function(config){
41003     /**
41004      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41005      * @type Roo.TabPanel
41006      */
41007     this.tabPanel = config.panel;
41008     /**
41009      * The id for this TabPanelItem
41010      * @type String
41011      */
41012     this.id = config.id;
41013     /** @private */
41014     this.disabled = false;
41015     /** @private */
41016     this.text = config.text;
41017     /** @private */
41018     this.loaded = false;
41019     this.closable = config.closable;
41020
41021     /**
41022      * The body element for this TabPanelItem.
41023      * @type Roo.Element
41024      */
41025     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41026     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41027     this.bodyEl.setStyle("display", "block");
41028     this.bodyEl.setStyle("zoom", "1");
41029     //this.hideAction();
41030
41031     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41032     /** @private */
41033     this.el = Roo.get(els.el);
41034     this.inner = Roo.get(els.inner, true);
41035      this.textEl = Roo.bootstrap.version == 4 ?
41036         this.el : Roo.get(this.el.dom.firstChild, true);
41037
41038     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41039     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41040
41041     
41042 //    this.el.on("mousedown", this.onTabMouseDown, this);
41043     this.el.on("click", this.onTabClick, this);
41044     /** @private */
41045     if(config.closable){
41046         var c = Roo.get(els.close, true);
41047         c.dom.title = this.closeText;
41048         c.addClassOnOver("close-over");
41049         c.on("click", this.closeClick, this);
41050      }
41051
41052     this.addEvents({
41053          /**
41054          * @event activate
41055          * Fires when this tab becomes the active tab.
41056          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41057          * @param {Roo.TabPanelItem} this
41058          */
41059         "activate": true,
41060         /**
41061          * @event beforeclose
41062          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41063          * @param {Roo.TabPanelItem} this
41064          * @param {Object} e Set cancel to true on this object to cancel the close.
41065          */
41066         "beforeclose": true,
41067         /**
41068          * @event close
41069          * Fires when this tab is closed.
41070          * @param {Roo.TabPanelItem} this
41071          */
41072          "close": true,
41073         /**
41074          * @event deactivate
41075          * Fires when this tab is no longer the active tab.
41076          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41077          * @param {Roo.TabPanelItem} this
41078          */
41079          "deactivate" : true
41080     });
41081     this.hidden = false;
41082
41083     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41084 };
41085
41086 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41087            {
41088     purgeListeners : function(){
41089        Roo.util.Observable.prototype.purgeListeners.call(this);
41090        this.el.removeAllListeners();
41091     },
41092     /**
41093      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41094      */
41095     show : function(){
41096         this.status_node.addClass("active");
41097         this.showAction();
41098         if(Roo.isOpera){
41099             this.tabPanel.stripWrap.repaint();
41100         }
41101         this.fireEvent("activate", this.tabPanel, this);
41102     },
41103
41104     /**
41105      * Returns true if this tab is the active tab.
41106      * @return {Boolean}
41107      */
41108     isActive : function(){
41109         return this.tabPanel.getActiveTab() == this;
41110     },
41111
41112     /**
41113      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41114      */
41115     hide : function(){
41116         this.status_node.removeClass("active");
41117         this.hideAction();
41118         this.fireEvent("deactivate", this.tabPanel, this);
41119     },
41120
41121     hideAction : function(){
41122         this.bodyEl.hide();
41123         this.bodyEl.setStyle("position", "absolute");
41124         this.bodyEl.setLeft("-20000px");
41125         this.bodyEl.setTop("-20000px");
41126     },
41127
41128     showAction : function(){
41129         this.bodyEl.setStyle("position", "relative");
41130         this.bodyEl.setTop("");
41131         this.bodyEl.setLeft("");
41132         this.bodyEl.show();
41133     },
41134
41135     /**
41136      * Set the tooltip for the tab.
41137      * @param {String} tooltip The tab's tooltip
41138      */
41139     setTooltip : function(text){
41140         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41141             this.textEl.dom.qtip = text;
41142             this.textEl.dom.removeAttribute('title');
41143         }else{
41144             this.textEl.dom.title = text;
41145         }
41146     },
41147
41148     onTabClick : function(e){
41149         e.preventDefault();
41150         this.tabPanel.activate(this.id);
41151     },
41152
41153     onTabMouseDown : function(e){
41154         e.preventDefault();
41155         this.tabPanel.activate(this.id);
41156     },
41157 /*
41158     getWidth : function(){
41159         return this.inner.getWidth();
41160     },
41161
41162     setWidth : function(width){
41163         var iwidth = width - this.linode.getPadding("lr");
41164         this.inner.setWidth(iwidth);
41165         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41166         this.linode.setWidth(width);
41167     },
41168 */
41169     /**
41170      * Show or hide the tab
41171      * @param {Boolean} hidden True to hide or false to show.
41172      */
41173     setHidden : function(hidden){
41174         this.hidden = hidden;
41175         this.linode.setStyle("display", hidden ? "none" : "");
41176     },
41177
41178     /**
41179      * Returns true if this tab is "hidden"
41180      * @return {Boolean}
41181      */
41182     isHidden : function(){
41183         return this.hidden;
41184     },
41185
41186     /**
41187      * Returns the text for this tab
41188      * @return {String}
41189      */
41190     getText : function(){
41191         return this.text;
41192     },
41193     /*
41194     autoSize : function(){
41195         //this.el.beginMeasure();
41196         this.textEl.setWidth(1);
41197         /*
41198          *  #2804 [new] Tabs in Roojs
41199          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41200          */
41201         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41202         //this.el.endMeasure();
41203     //},
41204
41205     /**
41206      * Sets the text for the tab (Note: this also sets the tooltip text)
41207      * @param {String} text The tab's text and tooltip
41208      */
41209     setText : function(text){
41210         this.text = text;
41211         this.textEl.update(text);
41212         this.setTooltip(text);
41213         //if(!this.tabPanel.resizeTabs){
41214         //    this.autoSize();
41215         //}
41216     },
41217     /**
41218      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41219      */
41220     activate : function(){
41221         this.tabPanel.activate(this.id);
41222     },
41223
41224     /**
41225      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41226      */
41227     disable : function(){
41228         if(this.tabPanel.active != this){
41229             this.disabled = true;
41230             this.status_node.addClass("disabled");
41231         }
41232     },
41233
41234     /**
41235      * Enables this TabPanelItem if it was previously disabled.
41236      */
41237     enable : function(){
41238         this.disabled = false;
41239         this.status_node.removeClass("disabled");
41240     },
41241
41242     /**
41243      * Sets the content for this TabPanelItem.
41244      * @param {String} content The content
41245      * @param {Boolean} loadScripts true to look for and load scripts
41246      */
41247     setContent : function(content, loadScripts){
41248         this.bodyEl.update(content, loadScripts);
41249     },
41250
41251     /**
41252      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41253      * @return {Roo.UpdateManager} The UpdateManager
41254      */
41255     getUpdateManager : function(){
41256         return this.bodyEl.getUpdateManager();
41257     },
41258
41259     /**
41260      * Set a URL to be used to load the content for this TabPanelItem.
41261      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41262      * @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)
41263      * @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)
41264      * @return {Roo.UpdateManager} The UpdateManager
41265      */
41266     setUrl : function(url, params, loadOnce){
41267         if(this.refreshDelegate){
41268             this.un('activate', this.refreshDelegate);
41269         }
41270         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41271         this.on("activate", this.refreshDelegate);
41272         return this.bodyEl.getUpdateManager();
41273     },
41274
41275     /** @private */
41276     _handleRefresh : function(url, params, loadOnce){
41277         if(!loadOnce || !this.loaded){
41278             var updater = this.bodyEl.getUpdateManager();
41279             updater.update(url, params, this._setLoaded.createDelegate(this));
41280         }
41281     },
41282
41283     /**
41284      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41285      *   Will fail silently if the setUrl method has not been called.
41286      *   This does not activate the panel, just updates its content.
41287      */
41288     refresh : function(){
41289         if(this.refreshDelegate){
41290            this.loaded = false;
41291            this.refreshDelegate();
41292         }
41293     },
41294
41295     /** @private */
41296     _setLoaded : function(){
41297         this.loaded = true;
41298     },
41299
41300     /** @private */
41301     closeClick : function(e){
41302         var o = {};
41303         e.stopEvent();
41304         this.fireEvent("beforeclose", this, o);
41305         if(o.cancel !== true){
41306             this.tabPanel.removeTab(this.id);
41307         }
41308     },
41309     /**
41310      * The text displayed in the tooltip for the close icon.
41311      * @type String
41312      */
41313     closeText : "Close this tab"
41314 });
41315 /**
41316 *    This script refer to:
41317 *    Title: International Telephone Input
41318 *    Author: Jack O'Connor
41319 *    Code version:  v12.1.12
41320 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41321 **/
41322
41323 Roo.bootstrap.PhoneInputData = function() {
41324     var d = [
41325       [
41326         "Afghanistan (‫افغانستان‬‎)",
41327         "af",
41328         "93"
41329       ],
41330       [
41331         "Albania (Shqipëri)",
41332         "al",
41333         "355"
41334       ],
41335       [
41336         "Algeria (‫الجزائر‬‎)",
41337         "dz",
41338         "213"
41339       ],
41340       [
41341         "American Samoa",
41342         "as",
41343         "1684"
41344       ],
41345       [
41346         "Andorra",
41347         "ad",
41348         "376"
41349       ],
41350       [
41351         "Angola",
41352         "ao",
41353         "244"
41354       ],
41355       [
41356         "Anguilla",
41357         "ai",
41358         "1264"
41359       ],
41360       [
41361         "Antigua and Barbuda",
41362         "ag",
41363         "1268"
41364       ],
41365       [
41366         "Argentina",
41367         "ar",
41368         "54"
41369       ],
41370       [
41371         "Armenia (Հայաստան)",
41372         "am",
41373         "374"
41374       ],
41375       [
41376         "Aruba",
41377         "aw",
41378         "297"
41379       ],
41380       [
41381         "Australia",
41382         "au",
41383         "61",
41384         0
41385       ],
41386       [
41387         "Austria (Österreich)",
41388         "at",
41389         "43"
41390       ],
41391       [
41392         "Azerbaijan (Azərbaycan)",
41393         "az",
41394         "994"
41395       ],
41396       [
41397         "Bahamas",
41398         "bs",
41399         "1242"
41400       ],
41401       [
41402         "Bahrain (‫البحرين‬‎)",
41403         "bh",
41404         "973"
41405       ],
41406       [
41407         "Bangladesh (বাংলাদেশ)",
41408         "bd",
41409         "880"
41410       ],
41411       [
41412         "Barbados",
41413         "bb",
41414         "1246"
41415       ],
41416       [
41417         "Belarus (Беларусь)",
41418         "by",
41419         "375"
41420       ],
41421       [
41422         "Belgium (België)",
41423         "be",
41424         "32"
41425       ],
41426       [
41427         "Belize",
41428         "bz",
41429         "501"
41430       ],
41431       [
41432         "Benin (Bénin)",
41433         "bj",
41434         "229"
41435       ],
41436       [
41437         "Bermuda",
41438         "bm",
41439         "1441"
41440       ],
41441       [
41442         "Bhutan (འབྲུག)",
41443         "bt",
41444         "975"
41445       ],
41446       [
41447         "Bolivia",
41448         "bo",
41449         "591"
41450       ],
41451       [
41452         "Bosnia and Herzegovina (Босна и Херцеговина)",
41453         "ba",
41454         "387"
41455       ],
41456       [
41457         "Botswana",
41458         "bw",
41459         "267"
41460       ],
41461       [
41462         "Brazil (Brasil)",
41463         "br",
41464         "55"
41465       ],
41466       [
41467         "British Indian Ocean Territory",
41468         "io",
41469         "246"
41470       ],
41471       [
41472         "British Virgin Islands",
41473         "vg",
41474         "1284"
41475       ],
41476       [
41477         "Brunei",
41478         "bn",
41479         "673"
41480       ],
41481       [
41482         "Bulgaria (България)",
41483         "bg",
41484         "359"
41485       ],
41486       [
41487         "Burkina Faso",
41488         "bf",
41489         "226"
41490       ],
41491       [
41492         "Burundi (Uburundi)",
41493         "bi",
41494         "257"
41495       ],
41496       [
41497         "Cambodia (កម្ពុជា)",
41498         "kh",
41499         "855"
41500       ],
41501       [
41502         "Cameroon (Cameroun)",
41503         "cm",
41504         "237"
41505       ],
41506       [
41507         "Canada",
41508         "ca",
41509         "1",
41510         1,
41511         ["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"]
41512       ],
41513       [
41514         "Cape Verde (Kabu Verdi)",
41515         "cv",
41516         "238"
41517       ],
41518       [
41519         "Caribbean Netherlands",
41520         "bq",
41521         "599",
41522         1
41523       ],
41524       [
41525         "Cayman Islands",
41526         "ky",
41527         "1345"
41528       ],
41529       [
41530         "Central African Republic (République centrafricaine)",
41531         "cf",
41532         "236"
41533       ],
41534       [
41535         "Chad (Tchad)",
41536         "td",
41537         "235"
41538       ],
41539       [
41540         "Chile",
41541         "cl",
41542         "56"
41543       ],
41544       [
41545         "China (中国)",
41546         "cn",
41547         "86"
41548       ],
41549       [
41550         "Christmas Island",
41551         "cx",
41552         "61",
41553         2
41554       ],
41555       [
41556         "Cocos (Keeling) Islands",
41557         "cc",
41558         "61",
41559         1
41560       ],
41561       [
41562         "Colombia",
41563         "co",
41564         "57"
41565       ],
41566       [
41567         "Comoros (‫جزر القمر‬‎)",
41568         "km",
41569         "269"
41570       ],
41571       [
41572         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41573         "cd",
41574         "243"
41575       ],
41576       [
41577         "Congo (Republic) (Congo-Brazzaville)",
41578         "cg",
41579         "242"
41580       ],
41581       [
41582         "Cook Islands",
41583         "ck",
41584         "682"
41585       ],
41586       [
41587         "Costa Rica",
41588         "cr",
41589         "506"
41590       ],
41591       [
41592         "Côte d’Ivoire",
41593         "ci",
41594         "225"
41595       ],
41596       [
41597         "Croatia (Hrvatska)",
41598         "hr",
41599         "385"
41600       ],
41601       [
41602         "Cuba",
41603         "cu",
41604         "53"
41605       ],
41606       [
41607         "Curaçao",
41608         "cw",
41609         "599",
41610         0
41611       ],
41612       [
41613         "Cyprus (Κύπρος)",
41614         "cy",
41615         "357"
41616       ],
41617       [
41618         "Czech Republic (Česká republika)",
41619         "cz",
41620         "420"
41621       ],
41622       [
41623         "Denmark (Danmark)",
41624         "dk",
41625         "45"
41626       ],
41627       [
41628         "Djibouti",
41629         "dj",
41630         "253"
41631       ],
41632       [
41633         "Dominica",
41634         "dm",
41635         "1767"
41636       ],
41637       [
41638         "Dominican Republic (República Dominicana)",
41639         "do",
41640         "1",
41641         2,
41642         ["809", "829", "849"]
41643       ],
41644       [
41645         "Ecuador",
41646         "ec",
41647         "593"
41648       ],
41649       [
41650         "Egypt (‫مصر‬‎)",
41651         "eg",
41652         "20"
41653       ],
41654       [
41655         "El Salvador",
41656         "sv",
41657         "503"
41658       ],
41659       [
41660         "Equatorial Guinea (Guinea Ecuatorial)",
41661         "gq",
41662         "240"
41663       ],
41664       [
41665         "Eritrea",
41666         "er",
41667         "291"
41668       ],
41669       [
41670         "Estonia (Eesti)",
41671         "ee",
41672         "372"
41673       ],
41674       [
41675         "Ethiopia",
41676         "et",
41677         "251"
41678       ],
41679       [
41680         "Falkland Islands (Islas Malvinas)",
41681         "fk",
41682         "500"
41683       ],
41684       [
41685         "Faroe Islands (Føroyar)",
41686         "fo",
41687         "298"
41688       ],
41689       [
41690         "Fiji",
41691         "fj",
41692         "679"
41693       ],
41694       [
41695         "Finland (Suomi)",
41696         "fi",
41697         "358",
41698         0
41699       ],
41700       [
41701         "France",
41702         "fr",
41703         "33"
41704       ],
41705       [
41706         "French Guiana (Guyane française)",
41707         "gf",
41708         "594"
41709       ],
41710       [
41711         "French Polynesia (Polynésie française)",
41712         "pf",
41713         "689"
41714       ],
41715       [
41716         "Gabon",
41717         "ga",
41718         "241"
41719       ],
41720       [
41721         "Gambia",
41722         "gm",
41723         "220"
41724       ],
41725       [
41726         "Georgia (საქართველო)",
41727         "ge",
41728         "995"
41729       ],
41730       [
41731         "Germany (Deutschland)",
41732         "de",
41733         "49"
41734       ],
41735       [
41736         "Ghana (Gaana)",
41737         "gh",
41738         "233"
41739       ],
41740       [
41741         "Gibraltar",
41742         "gi",
41743         "350"
41744       ],
41745       [
41746         "Greece (Ελλάδα)",
41747         "gr",
41748         "30"
41749       ],
41750       [
41751         "Greenland (Kalaallit Nunaat)",
41752         "gl",
41753         "299"
41754       ],
41755       [
41756         "Grenada",
41757         "gd",
41758         "1473"
41759       ],
41760       [
41761         "Guadeloupe",
41762         "gp",
41763         "590",
41764         0
41765       ],
41766       [
41767         "Guam",
41768         "gu",
41769         "1671"
41770       ],
41771       [
41772         "Guatemala",
41773         "gt",
41774         "502"
41775       ],
41776       [
41777         "Guernsey",
41778         "gg",
41779         "44",
41780         1
41781       ],
41782       [
41783         "Guinea (Guinée)",
41784         "gn",
41785         "224"
41786       ],
41787       [
41788         "Guinea-Bissau (Guiné Bissau)",
41789         "gw",
41790         "245"
41791       ],
41792       [
41793         "Guyana",
41794         "gy",
41795         "592"
41796       ],
41797       [
41798         "Haiti",
41799         "ht",
41800         "509"
41801       ],
41802       [
41803         "Honduras",
41804         "hn",
41805         "504"
41806       ],
41807       [
41808         "Hong Kong (香港)",
41809         "hk",
41810         "852"
41811       ],
41812       [
41813         "Hungary (Magyarország)",
41814         "hu",
41815         "36"
41816       ],
41817       [
41818         "Iceland (Ísland)",
41819         "is",
41820         "354"
41821       ],
41822       [
41823         "India (भारत)",
41824         "in",
41825         "91"
41826       ],
41827       [
41828         "Indonesia",
41829         "id",
41830         "62"
41831       ],
41832       [
41833         "Iran (‫ایران‬‎)",
41834         "ir",
41835         "98"
41836       ],
41837       [
41838         "Iraq (‫العراق‬‎)",
41839         "iq",
41840         "964"
41841       ],
41842       [
41843         "Ireland",
41844         "ie",
41845         "353"
41846       ],
41847       [
41848         "Isle of Man",
41849         "im",
41850         "44",
41851         2
41852       ],
41853       [
41854         "Israel (‫ישראל‬‎)",
41855         "il",
41856         "972"
41857       ],
41858       [
41859         "Italy (Italia)",
41860         "it",
41861         "39",
41862         0
41863       ],
41864       [
41865         "Jamaica",
41866         "jm",
41867         "1876"
41868       ],
41869       [
41870         "Japan (日本)",
41871         "jp",
41872         "81"
41873       ],
41874       [
41875         "Jersey",
41876         "je",
41877         "44",
41878         3
41879       ],
41880       [
41881         "Jordan (‫الأردن‬‎)",
41882         "jo",
41883         "962"
41884       ],
41885       [
41886         "Kazakhstan (Казахстан)",
41887         "kz",
41888         "7",
41889         1
41890       ],
41891       [
41892         "Kenya",
41893         "ke",
41894         "254"
41895       ],
41896       [
41897         "Kiribati",
41898         "ki",
41899         "686"
41900       ],
41901       [
41902         "Kosovo",
41903         "xk",
41904         "383"
41905       ],
41906       [
41907         "Kuwait (‫الكويت‬‎)",
41908         "kw",
41909         "965"
41910       ],
41911       [
41912         "Kyrgyzstan (Кыргызстан)",
41913         "kg",
41914         "996"
41915       ],
41916       [
41917         "Laos (ລາວ)",
41918         "la",
41919         "856"
41920       ],
41921       [
41922         "Latvia (Latvija)",
41923         "lv",
41924         "371"
41925       ],
41926       [
41927         "Lebanon (‫لبنان‬‎)",
41928         "lb",
41929         "961"
41930       ],
41931       [
41932         "Lesotho",
41933         "ls",
41934         "266"
41935       ],
41936       [
41937         "Liberia",
41938         "lr",
41939         "231"
41940       ],
41941       [
41942         "Libya (‫ليبيا‬‎)",
41943         "ly",
41944         "218"
41945       ],
41946       [
41947         "Liechtenstein",
41948         "li",
41949         "423"
41950       ],
41951       [
41952         "Lithuania (Lietuva)",
41953         "lt",
41954         "370"
41955       ],
41956       [
41957         "Luxembourg",
41958         "lu",
41959         "352"
41960       ],
41961       [
41962         "Macau (澳門)",
41963         "mo",
41964         "853"
41965       ],
41966       [
41967         "Macedonia (FYROM) (Македонија)",
41968         "mk",
41969         "389"
41970       ],
41971       [
41972         "Madagascar (Madagasikara)",
41973         "mg",
41974         "261"
41975       ],
41976       [
41977         "Malawi",
41978         "mw",
41979         "265"
41980       ],
41981       [
41982         "Malaysia",
41983         "my",
41984         "60"
41985       ],
41986       [
41987         "Maldives",
41988         "mv",
41989         "960"
41990       ],
41991       [
41992         "Mali",
41993         "ml",
41994         "223"
41995       ],
41996       [
41997         "Malta",
41998         "mt",
41999         "356"
42000       ],
42001       [
42002         "Marshall Islands",
42003         "mh",
42004         "692"
42005       ],
42006       [
42007         "Martinique",
42008         "mq",
42009         "596"
42010       ],
42011       [
42012         "Mauritania (‫موريتانيا‬‎)",
42013         "mr",
42014         "222"
42015       ],
42016       [
42017         "Mauritius (Moris)",
42018         "mu",
42019         "230"
42020       ],
42021       [
42022         "Mayotte",
42023         "yt",
42024         "262",
42025         1
42026       ],
42027       [
42028         "Mexico (México)",
42029         "mx",
42030         "52"
42031       ],
42032       [
42033         "Micronesia",
42034         "fm",
42035         "691"
42036       ],
42037       [
42038         "Moldova (Republica Moldova)",
42039         "md",
42040         "373"
42041       ],
42042       [
42043         "Monaco",
42044         "mc",
42045         "377"
42046       ],
42047       [
42048         "Mongolia (Монгол)",
42049         "mn",
42050         "976"
42051       ],
42052       [
42053         "Montenegro (Crna Gora)",
42054         "me",
42055         "382"
42056       ],
42057       [
42058         "Montserrat",
42059         "ms",
42060         "1664"
42061       ],
42062       [
42063         "Morocco (‫المغرب‬‎)",
42064         "ma",
42065         "212",
42066         0
42067       ],
42068       [
42069         "Mozambique (Moçambique)",
42070         "mz",
42071         "258"
42072       ],
42073       [
42074         "Myanmar (Burma) (မြန်မာ)",
42075         "mm",
42076         "95"
42077       ],
42078       [
42079         "Namibia (Namibië)",
42080         "na",
42081         "264"
42082       ],
42083       [
42084         "Nauru",
42085         "nr",
42086         "674"
42087       ],
42088       [
42089         "Nepal (नेपाल)",
42090         "np",
42091         "977"
42092       ],
42093       [
42094         "Netherlands (Nederland)",
42095         "nl",
42096         "31"
42097       ],
42098       [
42099         "New Caledonia (Nouvelle-Calédonie)",
42100         "nc",
42101         "687"
42102       ],
42103       [
42104         "New Zealand",
42105         "nz",
42106         "64"
42107       ],
42108       [
42109         "Nicaragua",
42110         "ni",
42111         "505"
42112       ],
42113       [
42114         "Niger (Nijar)",
42115         "ne",
42116         "227"
42117       ],
42118       [
42119         "Nigeria",
42120         "ng",
42121         "234"
42122       ],
42123       [
42124         "Niue",
42125         "nu",
42126         "683"
42127       ],
42128       [
42129         "Norfolk Island",
42130         "nf",
42131         "672"
42132       ],
42133       [
42134         "North Korea (조선 민주주의 인민 공화국)",
42135         "kp",
42136         "850"
42137       ],
42138       [
42139         "Northern Mariana Islands",
42140         "mp",
42141         "1670"
42142       ],
42143       [
42144         "Norway (Norge)",
42145         "no",
42146         "47",
42147         0
42148       ],
42149       [
42150         "Oman (‫عُمان‬‎)",
42151         "om",
42152         "968"
42153       ],
42154       [
42155         "Pakistan (‫پاکستان‬‎)",
42156         "pk",
42157         "92"
42158       ],
42159       [
42160         "Palau",
42161         "pw",
42162         "680"
42163       ],
42164       [
42165         "Palestine (‫فلسطين‬‎)",
42166         "ps",
42167         "970"
42168       ],
42169       [
42170         "Panama (Panamá)",
42171         "pa",
42172         "507"
42173       ],
42174       [
42175         "Papua New Guinea",
42176         "pg",
42177         "675"
42178       ],
42179       [
42180         "Paraguay",
42181         "py",
42182         "595"
42183       ],
42184       [
42185         "Peru (Perú)",
42186         "pe",
42187         "51"
42188       ],
42189       [
42190         "Philippines",
42191         "ph",
42192         "63"
42193       ],
42194       [
42195         "Poland (Polska)",
42196         "pl",
42197         "48"
42198       ],
42199       [
42200         "Portugal",
42201         "pt",
42202         "351"
42203       ],
42204       [
42205         "Puerto Rico",
42206         "pr",
42207         "1",
42208         3,
42209         ["787", "939"]
42210       ],
42211       [
42212         "Qatar (‫قطر‬‎)",
42213         "qa",
42214         "974"
42215       ],
42216       [
42217         "Réunion (La Réunion)",
42218         "re",
42219         "262",
42220         0
42221       ],
42222       [
42223         "Romania (România)",
42224         "ro",
42225         "40"
42226       ],
42227       [
42228         "Russia (Россия)",
42229         "ru",
42230         "7",
42231         0
42232       ],
42233       [
42234         "Rwanda",
42235         "rw",
42236         "250"
42237       ],
42238       [
42239         "Saint Barthélemy",
42240         "bl",
42241         "590",
42242         1
42243       ],
42244       [
42245         "Saint Helena",
42246         "sh",
42247         "290"
42248       ],
42249       [
42250         "Saint Kitts and Nevis",
42251         "kn",
42252         "1869"
42253       ],
42254       [
42255         "Saint Lucia",
42256         "lc",
42257         "1758"
42258       ],
42259       [
42260         "Saint Martin (Saint-Martin (partie française))",
42261         "mf",
42262         "590",
42263         2
42264       ],
42265       [
42266         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42267         "pm",
42268         "508"
42269       ],
42270       [
42271         "Saint Vincent and the Grenadines",
42272         "vc",
42273         "1784"
42274       ],
42275       [
42276         "Samoa",
42277         "ws",
42278         "685"
42279       ],
42280       [
42281         "San Marino",
42282         "sm",
42283         "378"
42284       ],
42285       [
42286         "São Tomé and Príncipe (São Tomé e Príncipe)",
42287         "st",
42288         "239"
42289       ],
42290       [
42291         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42292         "sa",
42293         "966"
42294       ],
42295       [
42296         "Senegal (Sénégal)",
42297         "sn",
42298         "221"
42299       ],
42300       [
42301         "Serbia (Србија)",
42302         "rs",
42303         "381"
42304       ],
42305       [
42306         "Seychelles",
42307         "sc",
42308         "248"
42309       ],
42310       [
42311         "Sierra Leone",
42312         "sl",
42313         "232"
42314       ],
42315       [
42316         "Singapore",
42317         "sg",
42318         "65"
42319       ],
42320       [
42321         "Sint Maarten",
42322         "sx",
42323         "1721"
42324       ],
42325       [
42326         "Slovakia (Slovensko)",
42327         "sk",
42328         "421"
42329       ],
42330       [
42331         "Slovenia (Slovenija)",
42332         "si",
42333         "386"
42334       ],
42335       [
42336         "Solomon Islands",
42337         "sb",
42338         "677"
42339       ],
42340       [
42341         "Somalia (Soomaaliya)",
42342         "so",
42343         "252"
42344       ],
42345       [
42346         "South Africa",
42347         "za",
42348         "27"
42349       ],
42350       [
42351         "South Korea (대한민국)",
42352         "kr",
42353         "82"
42354       ],
42355       [
42356         "South Sudan (‫جنوب السودان‬‎)",
42357         "ss",
42358         "211"
42359       ],
42360       [
42361         "Spain (España)",
42362         "es",
42363         "34"
42364       ],
42365       [
42366         "Sri Lanka (ශ්‍රී ලංකාව)",
42367         "lk",
42368         "94"
42369       ],
42370       [
42371         "Sudan (‫السودان‬‎)",
42372         "sd",
42373         "249"
42374       ],
42375       [
42376         "Suriname",
42377         "sr",
42378         "597"
42379       ],
42380       [
42381         "Svalbard and Jan Mayen",
42382         "sj",
42383         "47",
42384         1
42385       ],
42386       [
42387         "Swaziland",
42388         "sz",
42389         "268"
42390       ],
42391       [
42392         "Sweden (Sverige)",
42393         "se",
42394         "46"
42395       ],
42396       [
42397         "Switzerland (Schweiz)",
42398         "ch",
42399         "41"
42400       ],
42401       [
42402         "Syria (‫سوريا‬‎)",
42403         "sy",
42404         "963"
42405       ],
42406       [
42407         "Taiwan (台灣)",
42408         "tw",
42409         "886"
42410       ],
42411       [
42412         "Tajikistan",
42413         "tj",
42414         "992"
42415       ],
42416       [
42417         "Tanzania",
42418         "tz",
42419         "255"
42420       ],
42421       [
42422         "Thailand (ไทย)",
42423         "th",
42424         "66"
42425       ],
42426       [
42427         "Timor-Leste",
42428         "tl",
42429         "670"
42430       ],
42431       [
42432         "Togo",
42433         "tg",
42434         "228"
42435       ],
42436       [
42437         "Tokelau",
42438         "tk",
42439         "690"
42440       ],
42441       [
42442         "Tonga",
42443         "to",
42444         "676"
42445       ],
42446       [
42447         "Trinidad and Tobago",
42448         "tt",
42449         "1868"
42450       ],
42451       [
42452         "Tunisia (‫تونس‬‎)",
42453         "tn",
42454         "216"
42455       ],
42456       [
42457         "Turkey (Türkiye)",
42458         "tr",
42459         "90"
42460       ],
42461       [
42462         "Turkmenistan",
42463         "tm",
42464         "993"
42465       ],
42466       [
42467         "Turks and Caicos Islands",
42468         "tc",
42469         "1649"
42470       ],
42471       [
42472         "Tuvalu",
42473         "tv",
42474         "688"
42475       ],
42476       [
42477         "U.S. Virgin Islands",
42478         "vi",
42479         "1340"
42480       ],
42481       [
42482         "Uganda",
42483         "ug",
42484         "256"
42485       ],
42486       [
42487         "Ukraine (Україна)",
42488         "ua",
42489         "380"
42490       ],
42491       [
42492         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42493         "ae",
42494         "971"
42495       ],
42496       [
42497         "United Kingdom",
42498         "gb",
42499         "44",
42500         0
42501       ],
42502       [
42503         "United States",
42504         "us",
42505         "1",
42506         0
42507       ],
42508       [
42509         "Uruguay",
42510         "uy",
42511         "598"
42512       ],
42513       [
42514         "Uzbekistan (Oʻzbekiston)",
42515         "uz",
42516         "998"
42517       ],
42518       [
42519         "Vanuatu",
42520         "vu",
42521         "678"
42522       ],
42523       [
42524         "Vatican City (Città del Vaticano)",
42525         "va",
42526         "39",
42527         1
42528       ],
42529       [
42530         "Venezuela",
42531         "ve",
42532         "58"
42533       ],
42534       [
42535         "Vietnam (Việt Nam)",
42536         "vn",
42537         "84"
42538       ],
42539       [
42540         "Wallis and Futuna (Wallis-et-Futuna)",
42541         "wf",
42542         "681"
42543       ],
42544       [
42545         "Western Sahara (‫الصحراء الغربية‬‎)",
42546         "eh",
42547         "212",
42548         1
42549       ],
42550       [
42551         "Yemen (‫اليمن‬‎)",
42552         "ye",
42553         "967"
42554       ],
42555       [
42556         "Zambia",
42557         "zm",
42558         "260"
42559       ],
42560       [
42561         "Zimbabwe",
42562         "zw",
42563         "263"
42564       ],
42565       [
42566         "Åland Islands",
42567         "ax",
42568         "358",
42569         1
42570       ]
42571   ];
42572   
42573   return d;
42574 }/**
42575 *    This script refer to:
42576 *    Title: International Telephone Input
42577 *    Author: Jack O'Connor
42578 *    Code version:  v12.1.12
42579 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42580 **/
42581
42582 /**
42583  * @class Roo.bootstrap.PhoneInput
42584  * @extends Roo.bootstrap.TriggerField
42585  * An input with International dial-code selection
42586  
42587  * @cfg {String} defaultDialCode default '+852'
42588  * @cfg {Array} preferedCountries default []
42589   
42590  * @constructor
42591  * Create a new PhoneInput.
42592  * @param {Object} config Configuration options
42593  */
42594
42595 Roo.bootstrap.PhoneInput = function(config) {
42596     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42597 };
42598
42599 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42600         
42601         listWidth: undefined,
42602         
42603         selectedClass: 'active',
42604         
42605         invalidClass : "has-warning",
42606         
42607         validClass: 'has-success',
42608         
42609         allowed: '0123456789',
42610         
42611         max_length: 15,
42612         
42613         /**
42614          * @cfg {String} defaultDialCode The default dial code when initializing the input
42615          */
42616         defaultDialCode: '+852',
42617         
42618         /**
42619          * @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
42620          */
42621         preferedCountries: false,
42622         
42623         getAutoCreate : function()
42624         {
42625             var data = Roo.bootstrap.PhoneInputData();
42626             var align = this.labelAlign || this.parentLabelAlign();
42627             var id = Roo.id();
42628             
42629             this.allCountries = [];
42630             this.dialCodeMapping = [];
42631             
42632             for (var i = 0; i < data.length; i++) {
42633               var c = data[i];
42634               this.allCountries[i] = {
42635                 name: c[0],
42636                 iso2: c[1],
42637                 dialCode: c[2],
42638                 priority: c[3] || 0,
42639                 areaCodes: c[4] || null
42640               };
42641               this.dialCodeMapping[c[2]] = {
42642                   name: c[0],
42643                   iso2: c[1],
42644                   priority: c[3] || 0,
42645                   areaCodes: c[4] || null
42646               };
42647             }
42648             
42649             var cfg = {
42650                 cls: 'form-group',
42651                 cn: []
42652             };
42653             
42654             var input =  {
42655                 tag: 'input',
42656                 id : id,
42657                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42658                 maxlength: this.max_length,
42659                 cls : 'form-control tel-input',
42660                 autocomplete: 'new-password'
42661             };
42662             
42663             var hiddenInput = {
42664                 tag: 'input',
42665                 type: 'hidden',
42666                 cls: 'hidden-tel-input'
42667             };
42668             
42669             if (this.name) {
42670                 hiddenInput.name = this.name;
42671             }
42672             
42673             if (this.disabled) {
42674                 input.disabled = true;
42675             }
42676             
42677             var flag_container = {
42678                 tag: 'div',
42679                 cls: 'flag-box',
42680                 cn: [
42681                     {
42682                         tag: 'div',
42683                         cls: 'flag'
42684                     },
42685                     {
42686                         tag: 'div',
42687                         cls: 'caret'
42688                     }
42689                 ]
42690             };
42691             
42692             var box = {
42693                 tag: 'div',
42694                 cls: this.hasFeedback ? 'has-feedback' : '',
42695                 cn: [
42696                     hiddenInput,
42697                     input,
42698                     {
42699                         tag: 'input',
42700                         cls: 'dial-code-holder',
42701                         disabled: true
42702                     }
42703                 ]
42704             };
42705             
42706             var container = {
42707                 cls: 'roo-select2-container input-group',
42708                 cn: [
42709                     flag_container,
42710                     box
42711                 ]
42712             };
42713             
42714             if (this.fieldLabel.length) {
42715                 var indicator = {
42716                     tag: 'i',
42717                     tooltip: 'This field is required'
42718                 };
42719                 
42720                 var label = {
42721                     tag: 'label',
42722                     'for':  id,
42723                     cls: 'control-label',
42724                     cn: []
42725                 };
42726                 
42727                 var label_text = {
42728                     tag: 'span',
42729                     html: this.fieldLabel
42730                 };
42731                 
42732                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42733                 label.cn = [
42734                     indicator,
42735                     label_text
42736                 ];
42737                 
42738                 if(this.indicatorpos == 'right') {
42739                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42740                     label.cn = [
42741                         label_text,
42742                         indicator
42743                     ];
42744                 }
42745                 
42746                 if(align == 'left') {
42747                     container = {
42748                         tag: 'div',
42749                         cn: [
42750                             container
42751                         ]
42752                     };
42753                     
42754                     if(this.labelWidth > 12){
42755                         label.style = "width: " + this.labelWidth + 'px';
42756                     }
42757                     if(this.labelWidth < 13 && this.labelmd == 0){
42758                         this.labelmd = this.labelWidth;
42759                     }
42760                     if(this.labellg > 0){
42761                         label.cls += ' col-lg-' + this.labellg;
42762                         input.cls += ' col-lg-' + (12 - this.labellg);
42763                     }
42764                     if(this.labelmd > 0){
42765                         label.cls += ' col-md-' + this.labelmd;
42766                         container.cls += ' col-md-' + (12 - this.labelmd);
42767                     }
42768                     if(this.labelsm > 0){
42769                         label.cls += ' col-sm-' + this.labelsm;
42770                         container.cls += ' col-sm-' + (12 - this.labelsm);
42771                     }
42772                     if(this.labelxs > 0){
42773                         label.cls += ' col-xs-' + this.labelxs;
42774                         container.cls += ' col-xs-' + (12 - this.labelxs);
42775                     }
42776                 }
42777             }
42778             
42779             cfg.cn = [
42780                 label,
42781                 container
42782             ];
42783             
42784             var settings = this;
42785             
42786             ['xs','sm','md','lg'].map(function(size){
42787                 if (settings[size]) {
42788                     cfg.cls += ' col-' + size + '-' + settings[size];
42789                 }
42790             });
42791             
42792             this.store = new Roo.data.Store({
42793                 proxy : new Roo.data.MemoryProxy({}),
42794                 reader : new Roo.data.JsonReader({
42795                     fields : [
42796                         {
42797                             'name' : 'name',
42798                             'type' : 'string'
42799                         },
42800                         {
42801                             'name' : 'iso2',
42802                             'type' : 'string'
42803                         },
42804                         {
42805                             'name' : 'dialCode',
42806                             'type' : 'string'
42807                         },
42808                         {
42809                             'name' : 'priority',
42810                             'type' : 'string'
42811                         },
42812                         {
42813                             'name' : 'areaCodes',
42814                             'type' : 'string'
42815                         }
42816                     ]
42817                 })
42818             });
42819             
42820             if(!this.preferedCountries) {
42821                 this.preferedCountries = [
42822                     'hk',
42823                     'gb',
42824                     'us'
42825                 ];
42826             }
42827             
42828             var p = this.preferedCountries.reverse();
42829             
42830             if(p) {
42831                 for (var i = 0; i < p.length; i++) {
42832                     for (var j = 0; j < this.allCountries.length; j++) {
42833                         if(this.allCountries[j].iso2 == p[i]) {
42834                             var t = this.allCountries[j];
42835                             this.allCountries.splice(j,1);
42836                             this.allCountries.unshift(t);
42837                         }
42838                     } 
42839                 }
42840             }
42841             
42842             this.store.proxy.data = {
42843                 success: true,
42844                 data: this.allCountries
42845             };
42846             
42847             return cfg;
42848         },
42849         
42850         initEvents : function()
42851         {
42852             this.createList();
42853             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42854             
42855             this.indicator = this.indicatorEl();
42856             this.flag = this.flagEl();
42857             this.dialCodeHolder = this.dialCodeHolderEl();
42858             
42859             this.trigger = this.el.select('div.flag-box',true).first();
42860             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42861             
42862             var _this = this;
42863             
42864             (function(){
42865                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42866                 _this.list.setWidth(lw);
42867             }).defer(100);
42868             
42869             this.list.on('mouseover', this.onViewOver, this);
42870             this.list.on('mousemove', this.onViewMove, this);
42871             this.inputEl().on("keyup", this.onKeyUp, this);
42872             this.inputEl().on("keypress", this.onKeyPress, this);
42873             
42874             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42875
42876             this.view = new Roo.View(this.list, this.tpl, {
42877                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42878             });
42879             
42880             this.view.on('click', this.onViewClick, this);
42881             this.setValue(this.defaultDialCode);
42882         },
42883         
42884         onTriggerClick : function(e)
42885         {
42886             Roo.log('trigger click');
42887             if(this.disabled){
42888                 return;
42889             }
42890             
42891             if(this.isExpanded()){
42892                 this.collapse();
42893                 this.hasFocus = false;
42894             }else {
42895                 this.store.load({});
42896                 this.hasFocus = true;
42897                 this.expand();
42898             }
42899         },
42900         
42901         isExpanded : function()
42902         {
42903             return this.list.isVisible();
42904         },
42905         
42906         collapse : function()
42907         {
42908             if(!this.isExpanded()){
42909                 return;
42910             }
42911             this.list.hide();
42912             Roo.get(document).un('mousedown', this.collapseIf, this);
42913             Roo.get(document).un('mousewheel', this.collapseIf, this);
42914             this.fireEvent('collapse', this);
42915             this.validate();
42916         },
42917         
42918         expand : function()
42919         {
42920             Roo.log('expand');
42921
42922             if(this.isExpanded() || !this.hasFocus){
42923                 return;
42924             }
42925             
42926             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42927             this.list.setWidth(lw);
42928             
42929             this.list.show();
42930             this.restrictHeight();
42931             
42932             Roo.get(document).on('mousedown', this.collapseIf, this);
42933             Roo.get(document).on('mousewheel', this.collapseIf, this);
42934             
42935             this.fireEvent('expand', this);
42936         },
42937         
42938         restrictHeight : function()
42939         {
42940             this.list.alignTo(this.inputEl(), this.listAlign);
42941             this.list.alignTo(this.inputEl(), this.listAlign);
42942         },
42943         
42944         onViewOver : function(e, t)
42945         {
42946             if(this.inKeyMode){
42947                 return;
42948             }
42949             var item = this.view.findItemFromChild(t);
42950             
42951             if(item){
42952                 var index = this.view.indexOf(item);
42953                 this.select(index, false);
42954             }
42955         },
42956
42957         // private
42958         onViewClick : function(view, doFocus, el, e)
42959         {
42960             var index = this.view.getSelectedIndexes()[0];
42961             
42962             var r = this.store.getAt(index);
42963             
42964             if(r){
42965                 this.onSelect(r, index);
42966             }
42967             if(doFocus !== false && !this.blockFocus){
42968                 this.inputEl().focus();
42969             }
42970         },
42971         
42972         onViewMove : function(e, t)
42973         {
42974             this.inKeyMode = false;
42975         },
42976         
42977         select : function(index, scrollIntoView)
42978         {
42979             this.selectedIndex = index;
42980             this.view.select(index);
42981             if(scrollIntoView !== false){
42982                 var el = this.view.getNode(index);
42983                 if(el){
42984                     this.list.scrollChildIntoView(el, false);
42985                 }
42986             }
42987         },
42988         
42989         createList : function()
42990         {
42991             this.list = Roo.get(document.body).createChild({
42992                 tag: 'ul',
42993                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42994                 style: 'display:none'
42995             });
42996             
42997             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42998         },
42999         
43000         collapseIf : function(e)
43001         {
43002             var in_combo  = e.within(this.el);
43003             var in_list =  e.within(this.list);
43004             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43005             
43006             if (in_combo || in_list || is_list) {
43007                 return;
43008             }
43009             this.collapse();
43010         },
43011         
43012         onSelect : function(record, index)
43013         {
43014             if(this.fireEvent('beforeselect', this, record, index) !== false){
43015                 
43016                 this.setFlagClass(record.data.iso2);
43017                 this.setDialCode(record.data.dialCode);
43018                 this.hasFocus = false;
43019                 this.collapse();
43020                 this.fireEvent('select', this, record, index);
43021             }
43022         },
43023         
43024         flagEl : function()
43025         {
43026             var flag = this.el.select('div.flag',true).first();
43027             if(!flag){
43028                 return false;
43029             }
43030             return flag;
43031         },
43032         
43033         dialCodeHolderEl : function()
43034         {
43035             var d = this.el.select('input.dial-code-holder',true).first();
43036             if(!d){
43037                 return false;
43038             }
43039             return d;
43040         },
43041         
43042         setDialCode : function(v)
43043         {
43044             this.dialCodeHolder.dom.value = '+'+v;
43045         },
43046         
43047         setFlagClass : function(n)
43048         {
43049             this.flag.dom.className = 'flag '+n;
43050         },
43051         
43052         getValue : function()
43053         {
43054             var v = this.inputEl().getValue();
43055             if(this.dialCodeHolder) {
43056                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43057             }
43058             return v;
43059         },
43060         
43061         setValue : function(v)
43062         {
43063             var d = this.getDialCode(v);
43064             
43065             //invalid dial code
43066             if(v.length == 0 || !d || d.length == 0) {
43067                 if(this.rendered){
43068                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43069                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43070                 }
43071                 return;
43072             }
43073             
43074             //valid dial code
43075             this.setFlagClass(this.dialCodeMapping[d].iso2);
43076             this.setDialCode(d);
43077             this.inputEl().dom.value = v.replace('+'+d,'');
43078             this.hiddenEl().dom.value = this.getValue();
43079             
43080             this.validate();
43081         },
43082         
43083         getDialCode : function(v)
43084         {
43085             v = v ||  '';
43086             
43087             if (v.length == 0) {
43088                 return this.dialCodeHolder.dom.value;
43089             }
43090             
43091             var dialCode = "";
43092             if (v.charAt(0) != "+") {
43093                 return false;
43094             }
43095             var numericChars = "";
43096             for (var i = 1; i < v.length; i++) {
43097               var c = v.charAt(i);
43098               if (!isNaN(c)) {
43099                 numericChars += c;
43100                 if (this.dialCodeMapping[numericChars]) {
43101                   dialCode = v.substr(1, i);
43102                 }
43103                 if (numericChars.length == 4) {
43104                   break;
43105                 }
43106               }
43107             }
43108             return dialCode;
43109         },
43110         
43111         reset : function()
43112         {
43113             this.setValue(this.defaultDialCode);
43114             this.validate();
43115         },
43116         
43117         hiddenEl : function()
43118         {
43119             return this.el.select('input.hidden-tel-input',true).first();
43120         },
43121         
43122         // after setting val
43123         onKeyUp : function(e){
43124             this.setValue(this.getValue());
43125         },
43126         
43127         onKeyPress : function(e){
43128             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43129                 e.stopEvent();
43130             }
43131         }
43132         
43133 });
43134 /**
43135  * @class Roo.bootstrap.MoneyField
43136  * @extends Roo.bootstrap.ComboBox
43137  * Bootstrap MoneyField class
43138  * 
43139  * @constructor
43140  * Create a new MoneyField.
43141  * @param {Object} config Configuration options
43142  */
43143
43144 Roo.bootstrap.MoneyField = function(config) {
43145     
43146     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43147     
43148 };
43149
43150 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43151     
43152     /**
43153      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43154      */
43155     allowDecimals : true,
43156     /**
43157      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43158      */
43159     decimalSeparator : ".",
43160     /**
43161      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43162      */
43163     decimalPrecision : 0,
43164     /**
43165      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43166      */
43167     allowNegative : true,
43168     /**
43169      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43170      */
43171     allowZero: true,
43172     /**
43173      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43174      */
43175     minValue : Number.NEGATIVE_INFINITY,
43176     /**
43177      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43178      */
43179     maxValue : Number.MAX_VALUE,
43180     /**
43181      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43182      */
43183     minText : "The minimum value for this field is {0}",
43184     /**
43185      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43186      */
43187     maxText : "The maximum value for this field is {0}",
43188     /**
43189      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43190      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43191      */
43192     nanText : "{0} is not a valid number",
43193     /**
43194      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43195      */
43196     castInt : true,
43197     /**
43198      * @cfg {String} defaults currency of the MoneyField
43199      * value should be in lkey
43200      */
43201     defaultCurrency : false,
43202     /**
43203      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43204      */
43205     thousandsDelimiter : false,
43206     /**
43207      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43208      */
43209     max_length: false,
43210     
43211     inputlg : 9,
43212     inputmd : 9,
43213     inputsm : 9,
43214     inputxs : 6,
43215     
43216     store : false,
43217     
43218     getAutoCreate : function()
43219     {
43220         var align = this.labelAlign || this.parentLabelAlign();
43221         
43222         var id = Roo.id();
43223
43224         var cfg = {
43225             cls: 'form-group',
43226             cn: []
43227         };
43228
43229         var input =  {
43230             tag: 'input',
43231             id : id,
43232             cls : 'form-control roo-money-amount-input',
43233             autocomplete: 'new-password'
43234         };
43235         
43236         var hiddenInput = {
43237             tag: 'input',
43238             type: 'hidden',
43239             id: Roo.id(),
43240             cls: 'hidden-number-input'
43241         };
43242         
43243         if(this.max_length) {
43244             input.maxlength = this.max_length; 
43245         }
43246         
43247         if (this.name) {
43248             hiddenInput.name = this.name;
43249         }
43250
43251         if (this.disabled) {
43252             input.disabled = true;
43253         }
43254
43255         var clg = 12 - this.inputlg;
43256         var cmd = 12 - this.inputmd;
43257         var csm = 12 - this.inputsm;
43258         var cxs = 12 - this.inputxs;
43259         
43260         var container = {
43261             tag : 'div',
43262             cls : 'row roo-money-field',
43263             cn : [
43264                 {
43265                     tag : 'div',
43266                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43267                     cn : [
43268                         {
43269                             tag : 'div',
43270                             cls: 'roo-select2-container input-group',
43271                             cn: [
43272                                 {
43273                                     tag : 'input',
43274                                     cls : 'form-control roo-money-currency-input',
43275                                     autocomplete: 'new-password',
43276                                     readOnly : 1,
43277                                     name : this.currencyName
43278                                 },
43279                                 {
43280                                     tag :'span',
43281                                     cls : 'input-group-addon',
43282                                     cn : [
43283                                         {
43284                                             tag: 'span',
43285                                             cls: 'caret'
43286                                         }
43287                                     ]
43288                                 }
43289                             ]
43290                         }
43291                     ]
43292                 },
43293                 {
43294                     tag : 'div',
43295                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43296                     cn : [
43297                         {
43298                             tag: 'div',
43299                             cls: this.hasFeedback ? 'has-feedback' : '',
43300                             cn: [
43301                                 input
43302                             ]
43303                         }
43304                     ]
43305                 }
43306             ]
43307             
43308         };
43309         
43310         if (this.fieldLabel.length) {
43311             var indicator = {
43312                 tag: 'i',
43313                 tooltip: 'This field is required'
43314             };
43315
43316             var label = {
43317                 tag: 'label',
43318                 'for':  id,
43319                 cls: 'control-label',
43320                 cn: []
43321             };
43322
43323             var label_text = {
43324                 tag: 'span',
43325                 html: this.fieldLabel
43326             };
43327
43328             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43329             label.cn = [
43330                 indicator,
43331                 label_text
43332             ];
43333
43334             if(this.indicatorpos == 'right') {
43335                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43336                 label.cn = [
43337                     label_text,
43338                     indicator
43339                 ];
43340             }
43341
43342             if(align == 'left') {
43343                 container = {
43344                     tag: 'div',
43345                     cn: [
43346                         container
43347                     ]
43348                 };
43349
43350                 if(this.labelWidth > 12){
43351                     label.style = "width: " + this.labelWidth + 'px';
43352                 }
43353                 if(this.labelWidth < 13 && this.labelmd == 0){
43354                     this.labelmd = this.labelWidth;
43355                 }
43356                 if(this.labellg > 0){
43357                     label.cls += ' col-lg-' + this.labellg;
43358                     input.cls += ' col-lg-' + (12 - this.labellg);
43359                 }
43360                 if(this.labelmd > 0){
43361                     label.cls += ' col-md-' + this.labelmd;
43362                     container.cls += ' col-md-' + (12 - this.labelmd);
43363                 }
43364                 if(this.labelsm > 0){
43365                     label.cls += ' col-sm-' + this.labelsm;
43366                     container.cls += ' col-sm-' + (12 - this.labelsm);
43367                 }
43368                 if(this.labelxs > 0){
43369                     label.cls += ' col-xs-' + this.labelxs;
43370                     container.cls += ' col-xs-' + (12 - this.labelxs);
43371                 }
43372             }
43373         }
43374
43375         cfg.cn = [
43376             label,
43377             container,
43378             hiddenInput
43379         ];
43380         
43381         var settings = this;
43382
43383         ['xs','sm','md','lg'].map(function(size){
43384             if (settings[size]) {
43385                 cfg.cls += ' col-' + size + '-' + settings[size];
43386             }
43387         });
43388         
43389         return cfg;
43390     },
43391     
43392     initEvents : function()
43393     {
43394         this.indicator = this.indicatorEl();
43395         
43396         this.initCurrencyEvent();
43397         
43398         this.initNumberEvent();
43399     },
43400     
43401     initCurrencyEvent : function()
43402     {
43403         if (!this.store) {
43404             throw "can not find store for combo";
43405         }
43406         
43407         this.store = Roo.factory(this.store, Roo.data);
43408         this.store.parent = this;
43409         
43410         this.createList();
43411         
43412         this.triggerEl = this.el.select('.input-group-addon', true).first();
43413         
43414         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43415         
43416         var _this = this;
43417         
43418         (function(){
43419             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43420             _this.list.setWidth(lw);
43421         }).defer(100);
43422         
43423         this.list.on('mouseover', this.onViewOver, this);
43424         this.list.on('mousemove', this.onViewMove, this);
43425         this.list.on('scroll', this.onViewScroll, this);
43426         
43427         if(!this.tpl){
43428             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43429         }
43430         
43431         this.view = new Roo.View(this.list, this.tpl, {
43432             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43433         });
43434         
43435         this.view.on('click', this.onViewClick, this);
43436         
43437         this.store.on('beforeload', this.onBeforeLoad, this);
43438         this.store.on('load', this.onLoad, this);
43439         this.store.on('loadexception', this.onLoadException, this);
43440         
43441         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43442             "up" : function(e){
43443                 this.inKeyMode = true;
43444                 this.selectPrev();
43445             },
43446
43447             "down" : function(e){
43448                 if(!this.isExpanded()){
43449                     this.onTriggerClick();
43450                 }else{
43451                     this.inKeyMode = true;
43452                     this.selectNext();
43453                 }
43454             },
43455
43456             "enter" : function(e){
43457                 this.collapse();
43458                 
43459                 if(this.fireEvent("specialkey", this, e)){
43460                     this.onViewClick(false);
43461                 }
43462                 
43463                 return true;
43464             },
43465
43466             "esc" : function(e){
43467                 this.collapse();
43468             },
43469
43470             "tab" : function(e){
43471                 this.collapse();
43472                 
43473                 if(this.fireEvent("specialkey", this, e)){
43474                     this.onViewClick(false);
43475                 }
43476                 
43477                 return true;
43478             },
43479
43480             scope : this,
43481
43482             doRelay : function(foo, bar, hname){
43483                 if(hname == 'down' || this.scope.isExpanded()){
43484                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43485                 }
43486                 return true;
43487             },
43488
43489             forceKeyDown: true
43490         });
43491         
43492         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43493         
43494     },
43495     
43496     initNumberEvent : function(e)
43497     {
43498         this.inputEl().on("keydown" , this.fireKey,  this);
43499         this.inputEl().on("focus", this.onFocus,  this);
43500         this.inputEl().on("blur", this.onBlur,  this);
43501         
43502         this.inputEl().relayEvent('keyup', this);
43503         
43504         if(this.indicator){
43505             this.indicator.addClass('invisible');
43506         }
43507  
43508         this.originalValue = this.getValue();
43509         
43510         if(this.validationEvent == 'keyup'){
43511             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43512             this.inputEl().on('keyup', this.filterValidation, this);
43513         }
43514         else if(this.validationEvent !== false){
43515             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43516         }
43517         
43518         if(this.selectOnFocus){
43519             this.on("focus", this.preFocus, this);
43520             
43521         }
43522         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43523             this.inputEl().on("keypress", this.filterKeys, this);
43524         } else {
43525             this.inputEl().relayEvent('keypress', this);
43526         }
43527         
43528         var allowed = "0123456789";
43529         
43530         if(this.allowDecimals){
43531             allowed += this.decimalSeparator;
43532         }
43533         
43534         if(this.allowNegative){
43535             allowed += "-";
43536         }
43537         
43538         if(this.thousandsDelimiter) {
43539             allowed += ",";
43540         }
43541         
43542         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43543         
43544         var keyPress = function(e){
43545             
43546             var k = e.getKey();
43547             
43548             var c = e.getCharCode();
43549             
43550             if(
43551                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43552                     allowed.indexOf(String.fromCharCode(c)) === -1
43553             ){
43554                 e.stopEvent();
43555                 return;
43556             }
43557             
43558             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43559                 return;
43560             }
43561             
43562             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43563                 e.stopEvent();
43564             }
43565         };
43566         
43567         this.inputEl().on("keypress", keyPress, this);
43568         
43569     },
43570     
43571     onTriggerClick : function(e)
43572     {   
43573         if(this.disabled){
43574             return;
43575         }
43576         
43577         this.page = 0;
43578         this.loadNext = false;
43579         
43580         if(this.isExpanded()){
43581             this.collapse();
43582             return;
43583         }
43584         
43585         this.hasFocus = true;
43586         
43587         if(this.triggerAction == 'all') {
43588             this.doQuery(this.allQuery, true);
43589             return;
43590         }
43591         
43592         this.doQuery(this.getRawValue());
43593     },
43594     
43595     getCurrency : function()
43596     {   
43597         var v = this.currencyEl().getValue();
43598         
43599         return v;
43600     },
43601     
43602     restrictHeight : function()
43603     {
43604         this.list.alignTo(this.currencyEl(), this.listAlign);
43605         this.list.alignTo(this.currencyEl(), this.listAlign);
43606     },
43607     
43608     onViewClick : function(view, doFocus, el, e)
43609     {
43610         var index = this.view.getSelectedIndexes()[0];
43611         
43612         var r = this.store.getAt(index);
43613         
43614         if(r){
43615             this.onSelect(r, index);
43616         }
43617     },
43618     
43619     onSelect : function(record, index){
43620         
43621         if(this.fireEvent('beforeselect', this, record, index) !== false){
43622         
43623             this.setFromCurrencyData(index > -1 ? record.data : false);
43624             
43625             this.collapse();
43626             
43627             this.fireEvent('select', this, record, index);
43628         }
43629     },
43630     
43631     setFromCurrencyData : function(o)
43632     {
43633         var currency = '';
43634         
43635         this.lastCurrency = o;
43636         
43637         if (this.currencyField) {
43638             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43639         } else {
43640             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43641         }
43642         
43643         this.lastSelectionText = currency;
43644         
43645         //setting default currency
43646         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43647             this.setCurrency(this.defaultCurrency);
43648             return;
43649         }
43650         
43651         this.setCurrency(currency);
43652     },
43653     
43654     setFromData : function(o)
43655     {
43656         var c = {};
43657         
43658         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43659         
43660         this.setFromCurrencyData(c);
43661         
43662         var value = '';
43663         
43664         if (this.name) {
43665             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43666         } else {
43667             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43668         }
43669         
43670         this.setValue(value);
43671         
43672     },
43673     
43674     setCurrency : function(v)
43675     {   
43676         this.currencyValue = v;
43677         
43678         if(this.rendered){
43679             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43680             this.validate();
43681         }
43682     },
43683     
43684     setValue : function(v)
43685     {
43686         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43687         
43688         this.value = v;
43689         
43690         if(this.rendered){
43691             
43692             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43693             
43694             this.inputEl().dom.value = (v == '') ? '' :
43695                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43696             
43697             if(!this.allowZero && v === '0') {
43698                 this.hiddenEl().dom.value = '';
43699                 this.inputEl().dom.value = '';
43700             }
43701             
43702             this.validate();
43703         }
43704     },
43705     
43706     getRawValue : function()
43707     {
43708         var v = this.inputEl().getValue();
43709         
43710         return v;
43711     },
43712     
43713     getValue : function()
43714     {
43715         return this.fixPrecision(this.parseValue(this.getRawValue()));
43716     },
43717     
43718     parseValue : function(value)
43719     {
43720         if(this.thousandsDelimiter) {
43721             value += "";
43722             r = new RegExp(",", "g");
43723             value = value.replace(r, "");
43724         }
43725         
43726         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43727         return isNaN(value) ? '' : value;
43728         
43729     },
43730     
43731     fixPrecision : function(value)
43732     {
43733         if(this.thousandsDelimiter) {
43734             value += "";
43735             r = new RegExp(",", "g");
43736             value = value.replace(r, "");
43737         }
43738         
43739         var nan = isNaN(value);
43740         
43741         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43742             return nan ? '' : value;
43743         }
43744         return parseFloat(value).toFixed(this.decimalPrecision);
43745     },
43746     
43747     decimalPrecisionFcn : function(v)
43748     {
43749         return Math.floor(v);
43750     },
43751     
43752     validateValue : function(value)
43753     {
43754         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43755             return false;
43756         }
43757         
43758         var num = this.parseValue(value);
43759         
43760         if(isNaN(num)){
43761             this.markInvalid(String.format(this.nanText, value));
43762             return false;
43763         }
43764         
43765         if(num < this.minValue){
43766             this.markInvalid(String.format(this.minText, this.minValue));
43767             return false;
43768         }
43769         
43770         if(num > this.maxValue){
43771             this.markInvalid(String.format(this.maxText, this.maxValue));
43772             return false;
43773         }
43774         
43775         return true;
43776     },
43777     
43778     validate : function()
43779     {
43780         if(this.disabled || this.allowBlank){
43781             this.markValid();
43782             return true;
43783         }
43784         
43785         var currency = this.getCurrency();
43786         
43787         if(this.validateValue(this.getRawValue()) && currency.length){
43788             this.markValid();
43789             return true;
43790         }
43791         
43792         this.markInvalid();
43793         return false;
43794     },
43795     
43796     getName: function()
43797     {
43798         return this.name;
43799     },
43800     
43801     beforeBlur : function()
43802     {
43803         if(!this.castInt){
43804             return;
43805         }
43806         
43807         var v = this.parseValue(this.getRawValue());
43808         
43809         if(v || v == 0){
43810             this.setValue(v);
43811         }
43812     },
43813     
43814     onBlur : function()
43815     {
43816         this.beforeBlur();
43817         
43818         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43819             //this.el.removeClass(this.focusClass);
43820         }
43821         
43822         this.hasFocus = false;
43823         
43824         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43825             this.validate();
43826         }
43827         
43828         var v = this.getValue();
43829         
43830         if(String(v) !== String(this.startValue)){
43831             this.fireEvent('change', this, v, this.startValue);
43832         }
43833         
43834         this.fireEvent("blur", this);
43835     },
43836     
43837     inputEl : function()
43838     {
43839         return this.el.select('.roo-money-amount-input', true).first();
43840     },
43841     
43842     currencyEl : function()
43843     {
43844         return this.el.select('.roo-money-currency-input', true).first();
43845     },
43846     
43847     hiddenEl : function()
43848     {
43849         return this.el.select('input.hidden-number-input',true).first();
43850     }
43851     
43852 });/**
43853  * @class Roo.bootstrap.BezierSignature
43854  * @extends Roo.bootstrap.Component
43855  * Bootstrap BezierSignature class
43856  * This script refer to:
43857  *    Title: Signature Pad
43858  *    Author: szimek
43859  *    Availability: https://github.com/szimek/signature_pad
43860  *
43861  * @constructor
43862  * Create a new BezierSignature
43863  * @param {Object} config The config object
43864  */
43865
43866 Roo.bootstrap.BezierSignature = function(config){
43867     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43868     this.addEvents({
43869         "resize" : true
43870     });
43871 };
43872
43873 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43874 {
43875      
43876     curve_data: [],
43877     
43878     is_empty: true,
43879     
43880     mouse_btn_down: true,
43881     
43882     /**
43883      * @cfg {int} canvas height
43884      */
43885     canvas_height: '200px',
43886     
43887     /**
43888      * @cfg {float|function} Radius of a single dot.
43889      */ 
43890     dot_size: false,
43891     
43892     /**
43893      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43894      */
43895     min_width: 0.5,
43896     
43897     /**
43898      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43899      */
43900     max_width: 2.5,
43901     
43902     /**
43903      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43904      */
43905     throttle: 16,
43906     
43907     /**
43908      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43909      */
43910     min_distance: 5,
43911     
43912     /**
43913      * @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.
43914      */
43915     bg_color: 'rgba(0, 0, 0, 0)',
43916     
43917     /**
43918      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43919      */
43920     dot_color: 'black',
43921     
43922     /**
43923      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43924      */ 
43925     velocity_filter_weight: 0.7,
43926     
43927     /**
43928      * @cfg {function} Callback when stroke begin. 
43929      */
43930     onBegin: false,
43931     
43932     /**
43933      * @cfg {function} Callback when stroke end.
43934      */
43935     onEnd: false,
43936     
43937     getAutoCreate : function()
43938     {
43939         var cls = 'roo-signature column';
43940         
43941         if(this.cls){
43942             cls += ' ' + this.cls;
43943         }
43944         
43945         var col_sizes = [
43946             'lg',
43947             'md',
43948             'sm',
43949             'xs'
43950         ];
43951         
43952         for(var i = 0; i < col_sizes.length; i++) {
43953             if(this[col_sizes[i]]) {
43954                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43955             }
43956         }
43957         
43958         var cfg = {
43959             tag: 'div',
43960             cls: cls,
43961             cn: [
43962                 {
43963                     tag: 'div',
43964                     cls: 'roo-signature-body',
43965                     cn: [
43966                         {
43967                             tag: 'canvas',
43968                             cls: 'roo-signature-body-canvas',
43969                             height: this.canvas_height,
43970                             width: this.canvas_width
43971                         }
43972                     ]
43973                 },
43974                 {
43975                     tag: 'input',
43976                     type: 'file',
43977                     style: 'display: none'
43978                 }
43979             ]
43980         };
43981         
43982         return cfg;
43983     },
43984     
43985     initEvents: function() 
43986     {
43987         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43988         
43989         var canvas = this.canvasEl();
43990         
43991         // mouse && touch event swapping...
43992         canvas.dom.style.touchAction = 'none';
43993         canvas.dom.style.msTouchAction = 'none';
43994         
43995         this.mouse_btn_down = false;
43996         canvas.on('mousedown', this._handleMouseDown, this);
43997         canvas.on('mousemove', this._handleMouseMove, this);
43998         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43999         
44000         if (window.PointerEvent) {
44001             canvas.on('pointerdown', this._handleMouseDown, this);
44002             canvas.on('pointermove', this._handleMouseMove, this);
44003             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44004         }
44005         
44006         if ('ontouchstart' in window) {
44007             canvas.on('touchstart', this._handleTouchStart, this);
44008             canvas.on('touchmove', this._handleTouchMove, this);
44009             canvas.on('touchend', this._handleTouchEnd, this);
44010         }
44011         
44012         Roo.EventManager.onWindowResize(this.resize, this, true);
44013         
44014         // file input event
44015         this.fileEl().on('change', this.uploadImage, this);
44016         
44017         this.clear();
44018         
44019         this.resize();
44020     },
44021     
44022     resize: function(){
44023         
44024         var canvas = this.canvasEl().dom;
44025         var ctx = this.canvasElCtx();
44026         var img_data = false;
44027         
44028         if(canvas.width > 0) {
44029             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44030         }
44031         // setting canvas width will clean img data
44032         canvas.width = 0;
44033         
44034         var style = window.getComputedStyle ? 
44035             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44036             
44037         var padding_left = parseInt(style.paddingLeft) || 0;
44038         var padding_right = parseInt(style.paddingRight) || 0;
44039         
44040         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44041         
44042         if(img_data) {
44043             ctx.putImageData(img_data, 0, 0);
44044         }
44045     },
44046     
44047     _handleMouseDown: function(e)
44048     {
44049         if (e.browserEvent.which === 1) {
44050             this.mouse_btn_down = true;
44051             this.strokeBegin(e);
44052         }
44053     },
44054     
44055     _handleMouseMove: function (e)
44056     {
44057         if (this.mouse_btn_down) {
44058             this.strokeMoveUpdate(e);
44059         }
44060     },
44061     
44062     _handleMouseUp: function (e)
44063     {
44064         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44065             this.mouse_btn_down = false;
44066             this.strokeEnd(e);
44067         }
44068     },
44069     
44070     _handleTouchStart: function (e) {
44071         
44072         e.preventDefault();
44073         if (e.browserEvent.targetTouches.length === 1) {
44074             // var touch = e.browserEvent.changedTouches[0];
44075             // this.strokeBegin(touch);
44076             
44077              this.strokeBegin(e); // assume e catching the correct xy...
44078         }
44079     },
44080     
44081     _handleTouchMove: function (e) {
44082         e.preventDefault();
44083         // var touch = event.targetTouches[0];
44084         // _this._strokeMoveUpdate(touch);
44085         this.strokeMoveUpdate(e);
44086     },
44087     
44088     _handleTouchEnd: function (e) {
44089         var wasCanvasTouched = e.target === this.canvasEl().dom;
44090         if (wasCanvasTouched) {
44091             e.preventDefault();
44092             // var touch = event.changedTouches[0];
44093             // _this._strokeEnd(touch);
44094             this.strokeEnd(e);
44095         }
44096     },
44097     
44098     reset: function () {
44099         this._lastPoints = [];
44100         this._lastVelocity = 0;
44101         this._lastWidth = (this.min_width + this.max_width) / 2;
44102         this.canvasElCtx().fillStyle = this.dot_color;
44103     },
44104     
44105     strokeMoveUpdate: function(e)
44106     {
44107         this.strokeUpdate(e);
44108         
44109         if (this.throttle) {
44110             this.throttleStroke(this.strokeUpdate, this.throttle);
44111         }
44112         else {
44113             this.strokeUpdate(e);
44114         }
44115     },
44116     
44117     strokeBegin: function(e)
44118     {
44119         var newPointGroup = {
44120             color: this.dot_color,
44121             points: []
44122         };
44123         
44124         if (typeof this.onBegin === 'function') {
44125             this.onBegin(e);
44126         }
44127         
44128         this.curve_data.push(newPointGroup);
44129         this.reset();
44130         this.strokeUpdate(e);
44131     },
44132     
44133     strokeUpdate: function(e)
44134     {
44135         var rect = this.canvasEl().dom.getBoundingClientRect();
44136         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44137         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44138         var lastPoints = lastPointGroup.points;
44139         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44140         var isLastPointTooClose = lastPoint
44141             ? point.distanceTo(lastPoint) <= this.min_distance
44142             : false;
44143         var color = lastPointGroup.color;
44144         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44145             var curve = this.addPoint(point);
44146             if (!lastPoint) {
44147                 this.drawDot({color: color, point: point});
44148             }
44149             else if (curve) {
44150                 this.drawCurve({color: color, curve: curve});
44151             }
44152             lastPoints.push({
44153                 time: point.time,
44154                 x: point.x,
44155                 y: point.y
44156             });
44157         }
44158     },
44159     
44160     strokeEnd: function(e)
44161     {
44162         this.strokeUpdate(e);
44163         if (typeof this.onEnd === 'function') {
44164             this.onEnd(e);
44165         }
44166     },
44167     
44168     addPoint:  function (point) {
44169         var _lastPoints = this._lastPoints;
44170         _lastPoints.push(point);
44171         if (_lastPoints.length > 2) {
44172             if (_lastPoints.length === 3) {
44173                 _lastPoints.unshift(_lastPoints[0]);
44174             }
44175             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44176             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44177             _lastPoints.shift();
44178             return curve;
44179         }
44180         return null;
44181     },
44182     
44183     calculateCurveWidths: function (startPoint, endPoint) {
44184         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44185             (1 - this.velocity_filter_weight) * this._lastVelocity;
44186
44187         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44188         var widths = {
44189             end: newWidth,
44190             start: this._lastWidth
44191         };
44192         
44193         this._lastVelocity = velocity;
44194         this._lastWidth = newWidth;
44195         return widths;
44196     },
44197     
44198     drawDot: function (_a) {
44199         var color = _a.color, point = _a.point;
44200         var ctx = this.canvasElCtx();
44201         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44202         ctx.beginPath();
44203         this.drawCurveSegment(point.x, point.y, width);
44204         ctx.closePath();
44205         ctx.fillStyle = color;
44206         ctx.fill();
44207     },
44208     
44209     drawCurve: function (_a) {
44210         var color = _a.color, curve = _a.curve;
44211         var ctx = this.canvasElCtx();
44212         var widthDelta = curve.endWidth - curve.startWidth;
44213         var drawSteps = Math.floor(curve.length()) * 2;
44214         ctx.beginPath();
44215         ctx.fillStyle = color;
44216         for (var i = 0; i < drawSteps; i += 1) {
44217         var t = i / drawSteps;
44218         var tt = t * t;
44219         var ttt = tt * t;
44220         var u = 1 - t;
44221         var uu = u * u;
44222         var uuu = uu * u;
44223         var x = uuu * curve.startPoint.x;
44224         x += 3 * uu * t * curve.control1.x;
44225         x += 3 * u * tt * curve.control2.x;
44226         x += ttt * curve.endPoint.x;
44227         var y = uuu * curve.startPoint.y;
44228         y += 3 * uu * t * curve.control1.y;
44229         y += 3 * u * tt * curve.control2.y;
44230         y += ttt * curve.endPoint.y;
44231         var width = curve.startWidth + ttt * widthDelta;
44232         this.drawCurveSegment(x, y, width);
44233         }
44234         ctx.closePath();
44235         ctx.fill();
44236     },
44237     
44238     drawCurveSegment: function (x, y, width) {
44239         var ctx = this.canvasElCtx();
44240         ctx.moveTo(x, y);
44241         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44242         this.is_empty = false;
44243     },
44244     
44245     clear: function()
44246     {
44247         var ctx = this.canvasElCtx();
44248         var canvas = this.canvasEl().dom;
44249         ctx.fillStyle = this.bg_color;
44250         ctx.clearRect(0, 0, canvas.width, canvas.height);
44251         ctx.fillRect(0, 0, canvas.width, canvas.height);
44252         this.curve_data = [];
44253         this.reset();
44254         this.is_empty = true;
44255     },
44256     
44257     fileEl: function()
44258     {
44259         return  this.el.select('input',true).first();
44260     },
44261     
44262     canvasEl: function()
44263     {
44264         return this.el.select('canvas',true).first();
44265     },
44266     
44267     canvasElCtx: function()
44268     {
44269         return this.el.select('canvas',true).first().dom.getContext('2d');
44270     },
44271     
44272     getImage: function(type)
44273     {
44274         if(this.is_empty) {
44275             return false;
44276         }
44277         
44278         // encryption ?
44279         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44280     },
44281     
44282     drawFromImage: function(img_src)
44283     {
44284         var img = new Image();
44285         
44286         img.onload = function(){
44287             this.canvasElCtx().drawImage(img, 0, 0);
44288         }.bind(this);
44289         
44290         img.src = img_src;
44291         
44292         this.is_empty = false;
44293     },
44294     
44295     selectImage: function()
44296     {
44297         this.fileEl().dom.click();
44298     },
44299     
44300     uploadImage: function(e)
44301     {
44302         var reader = new FileReader();
44303         
44304         reader.onload = function(e){
44305             var img = new Image();
44306             img.onload = function(){
44307                 this.reset();
44308                 this.canvasElCtx().drawImage(img, 0, 0);
44309             }.bind(this);
44310             img.src = e.target.result;
44311         }.bind(this);
44312         
44313         reader.readAsDataURL(e.target.files[0]);
44314     },
44315     
44316     // Bezier Point Constructor
44317     Point: (function () {
44318         function Point(x, y, time) {
44319             this.x = x;
44320             this.y = y;
44321             this.time = time || Date.now();
44322         }
44323         Point.prototype.distanceTo = function (start) {
44324             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44325         };
44326         Point.prototype.equals = function (other) {
44327             return this.x === other.x && this.y === other.y && this.time === other.time;
44328         };
44329         Point.prototype.velocityFrom = function (start) {
44330             return this.time !== start.time
44331             ? this.distanceTo(start) / (this.time - start.time)
44332             : 0;
44333         };
44334         return Point;
44335     }()),
44336     
44337     
44338     // Bezier Constructor
44339     Bezier: (function () {
44340         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44341             this.startPoint = startPoint;
44342             this.control2 = control2;
44343             this.control1 = control1;
44344             this.endPoint = endPoint;
44345             this.startWidth = startWidth;
44346             this.endWidth = endWidth;
44347         }
44348         Bezier.fromPoints = function (points, widths, scope) {
44349             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44350             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44351             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44352         };
44353         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44354             var dx1 = s1.x - s2.x;
44355             var dy1 = s1.y - s2.y;
44356             var dx2 = s2.x - s3.x;
44357             var dy2 = s2.y - s3.y;
44358             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44359             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44360             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44361             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44362             var dxm = m1.x - m2.x;
44363             var dym = m1.y - m2.y;
44364             var k = l2 / (l1 + l2);
44365             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44366             var tx = s2.x - cm.x;
44367             var ty = s2.y - cm.y;
44368             return {
44369                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44370                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44371             };
44372         };
44373         Bezier.prototype.length = function () {
44374             var steps = 10;
44375             var length = 0;
44376             var px;
44377             var py;
44378             for (var i = 0; i <= steps; i += 1) {
44379                 var t = i / steps;
44380                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44381                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44382                 if (i > 0) {
44383                     var xdiff = cx - px;
44384                     var ydiff = cy - py;
44385                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44386                 }
44387                 px = cx;
44388                 py = cy;
44389             }
44390             return length;
44391         };
44392         Bezier.prototype.point = function (t, start, c1, c2, end) {
44393             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44394             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44395             + (3.0 * c2 * (1.0 - t) * t * t)
44396             + (end * t * t * t);
44397         };
44398         return Bezier;
44399     }()),
44400     
44401     throttleStroke: function(fn, wait) {
44402       if (wait === void 0) { wait = 250; }
44403       var previous = 0;
44404       var timeout = null;
44405       var result;
44406       var storedContext;
44407       var storedArgs;
44408       var later = function () {
44409           previous = Date.now();
44410           timeout = null;
44411           result = fn.apply(storedContext, storedArgs);
44412           if (!timeout) {
44413               storedContext = null;
44414               storedArgs = [];
44415           }
44416       };
44417       return function wrapper() {
44418           var args = [];
44419           for (var _i = 0; _i < arguments.length; _i++) {
44420               args[_i] = arguments[_i];
44421           }
44422           var now = Date.now();
44423           var remaining = wait - (now - previous);
44424           storedContext = this;
44425           storedArgs = args;
44426           if (remaining <= 0 || remaining > wait) {
44427               if (timeout) {
44428                   clearTimeout(timeout);
44429                   timeout = null;
44430               }
44431               previous = now;
44432               result = fn.apply(storedContext, storedArgs);
44433               if (!timeout) {
44434                   storedContext = null;
44435                   storedArgs = [];
44436               }
44437           }
44438           else if (!timeout) {
44439               timeout = window.setTimeout(later, remaining);
44440           }
44441           return result;
44442       };
44443   }
44444   
44445 });
44446
44447  
44448
44449